praxis-agent 0.1.2 → 0.3.0

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.
Files changed (110) hide show
  1. package/README.md +19 -3
  2. package/dist/application/session-service.d.ts +34 -0
  3. package/dist/application/session-service.d.ts.map +1 -1
  4. package/dist/application/session-service.js +646 -38
  5. package/dist/application/session-service.js.map +1 -1
  6. package/dist/cli/interactive.d.ts +71 -7
  7. package/dist/cli/interactive.d.ts.map +1 -1
  8. package/dist/cli/interactive.js +2688 -130
  9. package/dist/cli/interactive.js.map +1 -1
  10. package/dist/cli/protocol.d.ts +1 -0
  11. package/dist/cli/protocol.d.ts.map +1 -1
  12. package/dist/cli/protocol.js.map +1 -1
  13. package/dist/cli/tui/claude-style.d.ts +202 -0
  14. package/dist/cli/tui/claude-style.d.ts.map +1 -0
  15. package/dist/cli/tui/claude-style.js +509 -0
  16. package/dist/cli/tui/claude-style.js.map +1 -0
  17. package/dist/cli/tui/clipboard.d.ts +24 -0
  18. package/dist/cli/tui/clipboard.d.ts.map +1 -0
  19. package/dist/cli/tui/clipboard.js +153 -0
  20. package/dist/cli/tui/clipboard.js.map +1 -0
  21. package/dist/cli/tui/composer-editor.d.ts +19 -0
  22. package/dist/cli/tui/composer-editor.d.ts.map +1 -0
  23. package/dist/cli/tui/composer-editor.js +99 -0
  24. package/dist/cli/tui/composer-editor.js.map +1 -0
  25. package/dist/cli/tui/composer-images.d.ts +7 -0
  26. package/dist/cli/tui/composer-images.d.ts.map +1 -0
  27. package/dist/cli/tui/composer-images.js +48 -0
  28. package/dist/cli/tui/composer-images.js.map +1 -0
  29. package/dist/cli/tui/conversation-export.d.ts +6 -0
  30. package/dist/cli/tui/conversation-export.d.ts.map +1 -0
  31. package/dist/cli/tui/conversation-export.js +76 -0
  32. package/dist/cli/tui/conversation-export.js.map +1 -0
  33. package/dist/cli/tui/external-editor.d.ts +21 -0
  34. package/dist/cli/tui/external-editor.d.ts.map +1 -0
  35. package/dist/cli/tui/external-editor.js +144 -0
  36. package/dist/cli/tui/external-editor.js.map +1 -0
  37. package/dist/cli/tui/file-picker.d.ts +33 -0
  38. package/dist/cli/tui/file-picker.d.ts.map +1 -0
  39. package/dist/cli/tui/file-picker.js +117 -0
  40. package/dist/cli/tui/file-picker.js.map +1 -0
  41. package/dist/cli/tui/git-diff.d.ts +14 -0
  42. package/dist/cli/tui/git-diff.d.ts.map +1 -0
  43. package/dist/cli/tui/git-diff.js +55 -0
  44. package/dist/cli/tui/git-diff.js.map +1 -0
  45. package/dist/cli/tui/keybindings.d.ts +14 -0
  46. package/dist/cli/tui/keybindings.d.ts.map +1 -0
  47. package/dist/cli/tui/keybindings.js +430 -0
  48. package/dist/cli/tui/keybindings.js.map +1 -0
  49. package/dist/cli/tui/permission-settings.d.ts +18 -0
  50. package/dist/cli/tui/permission-settings.d.ts.map +1 -0
  51. package/dist/cli/tui/permission-settings.js +109 -0
  52. package/dist/cli/tui/permission-settings.js.map +1 -0
  53. package/dist/cli/tui/slash-commands.d.ts +11 -0
  54. package/dist/cli/tui/slash-commands.d.ts.map +1 -0
  55. package/dist/cli/tui/slash-commands.js +183 -0
  56. package/dist/cli/tui/slash-commands.js.map +1 -0
  57. package/dist/cli/tui/terminal-suspend.d.ts +5 -0
  58. package/dist/cli/tui/terminal-suspend.d.ts.map +1 -0
  59. package/dist/cli/tui/terminal-suspend.js +9 -0
  60. package/dist/cli/tui/terminal-suspend.js.map +1 -0
  61. package/dist/cli/tui/workspace-directories.d.ts +3 -0
  62. package/dist/cli/tui/workspace-directories.d.ts.map +1 -0
  63. package/dist/cli/tui/workspace-directories.js +55 -0
  64. package/dist/cli/tui/workspace-directories.js.map +1 -0
  65. package/dist/cli.d.ts +24 -3
  66. package/dist/cli.d.ts.map +1 -1
  67. package/dist/cli.js +96 -39
  68. package/dist/cli.js.map +1 -1
  69. package/dist/compatibility/claude/compaction.d.ts +8 -0
  70. package/dist/compatibility/claude/compaction.d.ts.map +1 -1
  71. package/dist/compatibility/claude/compaction.js +45 -8
  72. package/dist/compatibility/claude/compaction.js.map +1 -1
  73. package/dist/compatibility/claude/fork.d.ts.map +1 -1
  74. package/dist/compatibility/claude/fork.js +24 -6
  75. package/dist/compatibility/claude/fork.js.map +1 -1
  76. package/dist/compatibility/claude/history.d.ts.map +1 -1
  77. package/dist/compatibility/claude/history.js +41 -1
  78. package/dist/compatibility/claude/history.js.map +1 -1
  79. package/dist/compatibility/claude/projection.d.ts +29 -1
  80. package/dist/compatibility/claude/projection.d.ts.map +1 -1
  81. package/dist/compatibility/claude/projection.js +132 -2
  82. package/dist/compatibility/claude/projection.js.map +1 -1
  83. package/dist/compatibility/claude/schema.d.ts.map +1 -1
  84. package/dist/compatibility/claude/schema.js +34 -7
  85. package/dist/compatibility/claude/schema.js.map +1 -1
  86. package/dist/compatibility/claude/translation.d.ts +7 -0
  87. package/dist/compatibility/claude/translation.d.ts.map +1 -1
  88. package/dist/compatibility/claude/translation.js +20 -0
  89. package/dist/compatibility/claude/translation.js.map +1 -1
  90. package/dist/core/runtime.d.ts +19 -0
  91. package/dist/core/runtime.d.ts.map +1 -1
  92. package/dist/core/runtime.js +30 -11
  93. package/dist/core/runtime.js.map +1 -1
  94. package/dist/extensions/claude-extensions.d.ts +12 -0
  95. package/dist/extensions/claude-extensions.d.ts.map +1 -1
  96. package/dist/extensions/claude-extensions.js +49 -0
  97. package/dist/extensions/claude-extensions.js.map +1 -1
  98. package/dist/hooks/claude-hook-tools.js +2 -2
  99. package/dist/hooks/claude-hook-tools.js.map +1 -1
  100. package/dist/persistence/claude-transcript-store.d.ts.map +1 -1
  101. package/dist/persistence/claude-transcript-store.js +4 -3
  102. package/dist/persistence/claude-transcript-store.js.map +1 -1
  103. package/dist/tools/claude-interactive-tools.d.ts +1 -0
  104. package/dist/tools/claude-interactive-tools.d.ts.map +1 -1
  105. package/dist/tools/claude-interactive-tools.js +12 -0
  106. package/dist/tools/claude-interactive-tools.js.map +1 -1
  107. package/dist/tools/local-tools.d.ts.map +1 -1
  108. package/dist/tools/local-tools.js +11 -0
  109. package/dist/tools/local-tools.js.map +1 -1
  110. package/package.json +3 -2
@@ -0,0 +1,509 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Box, Text, useStdout } from 'ink';
4
+ import { composerEditorSegments } from './composer-editor.js';
5
+ import { visiblePatchLines } from './git-diff.js';
6
+ const BRAND = '#D97757';
7
+ const ACCENT = '#B8A1FF';
8
+ const SPINNER = ['✳', '✢', '✣', '✤', '✥'];
9
+ export function useTerminalWidth(override) {
10
+ const { stdout } = useStdout();
11
+ const [width, setWidth] = useState(override ?? stdout.columns ?? 80);
12
+ useEffect(() => {
13
+ if (override !== undefined) {
14
+ setWidth(override);
15
+ return;
16
+ }
17
+ const resize = () => setWidth(stdout.columns ?? 80);
18
+ stdout.on('resize', resize);
19
+ return () => {
20
+ stdout.off('resize', resize);
21
+ };
22
+ }, [override, stdout]);
23
+ return Math.max(32, width);
24
+ }
25
+ function compactPath(cwd) {
26
+ const home = process.env.HOME;
27
+ return home && cwd.startsWith(`${home}/`)
28
+ ? `~/${cwd.slice(home.length + 1)}`
29
+ : cwd;
30
+ }
31
+ function permissionLabel(mode) {
32
+ switch (mode) {
33
+ case 'acceptEdits':
34
+ return 'accept edits on';
35
+ case 'bypassPermissions':
36
+ return 'bypass permissions on';
37
+ case 'dontAsk':
38
+ return 'dont ask mode';
39
+ case 'plan':
40
+ return 'plan mode';
41
+ case 'auto':
42
+ return 'auto mode';
43
+ default:
44
+ return 'permissions default';
45
+ }
46
+ }
47
+ export function WelcomePanel({ display, width, }) {
48
+ const panelWidth = Math.min(100, Math.max(32, width));
49
+ const wide = panelWidth >= 68;
50
+ return (_jsxs(Box, { borderStyle: "round", borderColor: "gray", flexDirection: "column", width: panelWidth, paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: BRAND, bold: true, children: "Praxis" }), _jsxs(Text, { children: [" Code v", display.version, " "] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(1, panelWidth - 24)) })] }), _jsxs(Box, { flexDirection: wide ? 'row' : 'column', marginTop: 1, children: [_jsxs(Box, { alignItems: wide ? 'center' : undefined, flexDirection: "column", width: wide ? '50%' : '100%', children: [_jsx(Text, { bold: true, children: "Welcome back!" }), _jsx(Text, { color: BRAND, bold: true, children: "\u2590\u259B\u2588\u2588\u2588\u259C\u258C" }), _jsx(Text, { color: BRAND, children: "\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u259B\u2598" }), _jsx(Text, { color: BRAND, children: " \u2598\u2598 \u259D\u259D" }), _jsxs(Text, { children: [display.model ?? 'provider default', display.effort ? (_jsxs(Text, { dimColor: true, children: [" \u00B7 ", display.effort, " effort"] })) : null] }), _jsx(Text, { dimColor: true, children: compactPath(display.cwd) })] }), _jsxs(Box, { flexDirection: "column", width: wide ? '50%' : '100%', marginTop: wide ? 0 : 1, children: [_jsx(Text, { bold: true, children: "Tips for getting started" }), _jsx(Text, { children: "Run /help for commands" }), _jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Text, { bold: true, children: "Shared with Claude Code" }), _jsx(Text, { children: "Sessions, memory, skills" })] })] })] }));
51
+ }
52
+ function InlineText({ text }) {
53
+ const tokens = text.split(/(`[^`]+`|\*\*[^*]+\*\*|\[[^\]]+\]\([^)]+\))/u);
54
+ return (_jsx(Text, { children: tokens.map((token, index) => {
55
+ if (token.startsWith('`') && token.endsWith('`'))
56
+ return (_jsx(Text, { color: "cyan", children: token.slice(1, -1) }, index));
57
+ if (token.startsWith('**') && token.endsWith('**'))
58
+ return (_jsx(Text, { bold: true, children: token.slice(2, -2) }, index));
59
+ const link = /^\[([^\]]+)\]\(([^)]+)\)$/u.exec(token);
60
+ if (link)
61
+ return (_jsx(Text, { color: "blue", underline: true, children: link[1] }, index));
62
+ return token;
63
+ }) }));
64
+ }
65
+ function MarkdownLine({ line }) {
66
+ if (line.startsWith('### '))
67
+ return (_jsx(Text, { bold: true, children: _jsx(InlineText, { text: line.slice(4) }) }));
68
+ if (line.startsWith('## '))
69
+ return (_jsx(Text, { bold: true, color: ACCENT, children: _jsx(InlineText, { text: line.slice(3) }) }));
70
+ if (line.startsWith('# '))
71
+ return (_jsx(Text, { bold: true, color: BRAND, children: _jsx(InlineText, { text: line.slice(2) }) }));
72
+ if (/^[-*] /u.test(line))
73
+ return (_jsxs(Text, { children: [' • ', _jsx(InlineText, { text: line.slice(2) })] }));
74
+ if (/^\d+\. /u.test(line))
75
+ return (_jsxs(Text, { children: [' ', _jsx(InlineText, { text: line })] }));
76
+ if (line.startsWith('> '))
77
+ return (_jsxs(Text, { dimColor: true, children: ['│ ', _jsx(InlineText, { text: line.slice(2) })] }));
78
+ return line ? _jsx(InlineText, { text: line }) : _jsx(Text, { children: " " });
79
+ }
80
+ function ToolResultText({ text }) {
81
+ return (_jsx(Box, { flexDirection: "column", children: text.split('\n').map((line, index) => (_jsx(Text, { ...(line.startsWith('+') && !line.startsWith('+++')
82
+ ? { color: 'green' }
83
+ : line.startsWith('-') && !line.startsWith('---')
84
+ ? { color: 'red' }
85
+ : line.startsWith('@@')
86
+ ? { color: 'cyan' }
87
+ : { dimColor: true }), children: line || ' ' }, index))) }));
88
+ }
89
+ function inputString(call, key) {
90
+ const value = call.input[key];
91
+ return typeof value === 'string' ? value : '';
92
+ }
93
+ function contentLines(text) {
94
+ const lines = text.split('\n');
95
+ if (lines.at(-1) === '')
96
+ lines.pop();
97
+ return lines;
98
+ }
99
+ function toolHeading(call) {
100
+ if (call.name === 'Edit')
101
+ return `Update(${inputString(call, 'file_path')})`;
102
+ if (call.name === 'Read')
103
+ return `Read(${inputString(call, 'file_path')})`;
104
+ if (call.name === 'Bash')
105
+ return `Bash(${inputString(call, 'command')})`;
106
+ return call.name;
107
+ }
108
+ function ToolTranscriptEntry({ call, detail, result, detailed, }) {
109
+ const displayResult = call.name === 'Read' &&
110
+ result?.text.startsWith('Wasted call — file unchanged since your last Read')
111
+ ? 'Unchanged since last read'
112
+ : (result?.text ?? '');
113
+ const resultLines = contentLines(displayResult);
114
+ if (call.name === 'Read' && result && !result.isError && !detailed) {
115
+ return (_jsxs(Text, { children: [' ', "Read 1 file ", _jsx(Text, { dimColor: true, children: "(ctrl+o to expand)" })] }));
116
+ }
117
+ const visible = detailed ? resultLines : resultLines.slice(0, 3);
118
+ const hidden = resultLines.length - visible.length;
119
+ const errorText = displayResult.length > 500
120
+ ? `${displayResult.slice(0, 497)}...`
121
+ : displayResult;
122
+ const oldLines = contentLines(inputString(call, 'old_string'));
123
+ const newLines = contentLines(inputString(call, 'new_string'));
124
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: ACCENT, children: "\u23FA" }), " ", _jsx(Text, { bold: true, children: toolHeading(call) })] }), !['Bash', 'Read', 'Edit'].includes(call.name) && detail ? (_jsxs(Text, { dimColor: true, children: [" ", detail] })) : null, result ? (_jsx(Box, { marginLeft: 2, flexDirection: "column", children: result.isError ? (_jsxs(Text, { color: "red", children: ["\u23BF Error: ", errorText] })) : call.name === 'Edit' ? (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: ["\u23BF Added ", newLines.length, " line", newLines.length === 1 ? '' : 's', ", removed ", oldLines.length, " line", oldLines.length === 1 ? '' : 's'] }), oldLines.map((line, index) => (_jsxs(Text, { color: "red", children: [' ', index + 1, " -", line] }, `old-${index}`))), newLines.map((line, index) => (_jsxs(Text, { color: "green", children: [' ', index + 1, " +", line] }, `new-${index}`)))] })) : (_jsxs(_Fragment, { children: [visible.map((line, index) => (_jsxs(Text, { dimColor: true, children: [index === 0 ? '⎿ ' : ' ', line || ' '] }, index))), hidden > 0 ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2026 +", hidden, " lines (ctrl+o to expand)"] })) : null] })) })) : null] }));
125
+ }
126
+ function ThinkingBlock({ text, active, expanded, screenReader, }) {
127
+ const summary = text.replace(/\s+/gu, ' ').trim();
128
+ const showFull = screenReader || active || expanded;
129
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { ...(!screenReader ? { color: ACCENT } : {}), dimColor: !screenReader, italic: !screenReader, children: [screenReader ? 'Thinking:' : '✻ ', active ? 'Thinking…' : 'Thought for a moment', !showFull && summary ? ` · ${summary.slice(0, 160)}` : ''] }), showFull && text ? (_jsx(Box, { marginLeft: screenReader ? 0 : 2, flexDirection: "column", children: _jsx(MarkdownText, { text: text }) })) : !screenReader && summary.length > 160 ? (_jsx(Text, { dimColor: true, children: " ctrl+o to expand thinking" })) : null] }));
130
+ }
131
+ function ContextUsageBlock({ usedTokens, contextWindowTokens, skills, screenReader, }) {
132
+ const totalTokens = Math.max(1, contextWindowTokens);
133
+ const compactBuffer = Math.round(totalTokens * 0.165);
134
+ const usable = Math.max(1, totalTokens - compactBuffer);
135
+ if (screenReader) {
136
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Context Usage" }), _jsxs(Text, { children: [usedTokens.toLocaleString(), " of ", totalTokens.toLocaleString(), " tokens"] }), _jsxs(Text, { children: ["Autocompact buffer: ", compactBuffer.toLocaleString(), " tokens"] }), _jsxs(Text, { children: ["Skills: ", skills.map(({ name }) => name).join(', ') || 'none'] })] }));
137
+ }
138
+ const usedCells = Math.min(100, Math.ceil((usedTokens / totalTokens) * 100));
139
+ const bufferCells = Math.min(100 - usedCells, Math.ceil((compactBuffer / totalTokens) * 100));
140
+ const cells = Array.from({ length: 100 }, (_, index) => index < usedCells ? '⛁' : index >= 100 - bufferCells ? '⛝' : '⛶');
141
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { bold: true, children: "Context Usage" }), _jsxs(Box, { children: [_jsx(Box, { flexDirection: "column", marginRight: 2, children: Array.from({ length: 10 }, (_, row) => (_jsx(Text, { children: cells.slice(row * 10, row * 10 + 10).join(' ') }, row))) }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [usedTokens.toLocaleString(), "/", totalTokens.toLocaleString(), " tokens"] }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Estimated usage by category" }), _jsxs(Text, { children: ["\u26C1 Skills:", ' ', skills
142
+ .reduce((total, skill) => total + skill.tokens, 0)
143
+ .toLocaleString(), ' ', "tokens"] }), _jsxs(Text, { children: ["\u26F6 Free space: ", Math.max(0, usable - usedTokens).toLocaleString()] }), _jsxs(Text, { children: ["\u26DD Autocompact buffer: ", compactBuffer.toLocaleString(), " tokens"] })] })] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Auto-compact window: ", totalTokens.toLocaleString(), " tokens"] }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Skills \u00B7 /skills" }), skills.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u2514 No skills loaded" })) : (skills.map((skill, index) => (_jsxs(Text, { dimColor: true, children: [index === skills.length - 1 ? '└' : '├', " ", skill.name, ": ~", skill.tokens, " tokens"] }, skill.name))))] }));
144
+ }
145
+ export function MarkdownText({ text }) {
146
+ const lines = text.split('\n');
147
+ let code = false;
148
+ return (_jsx(Box, { flexDirection: "column", children: lines.map((line, index) => {
149
+ if (line.startsWith('```')) {
150
+ code = !code;
151
+ return (_jsx(Text, { dimColor: true, children: code ? `╭─ ${line.slice(3) || 'code'}` : '╰─' }, index));
152
+ }
153
+ return code ? (_jsxs(Text, { color: "cyan", children: ["\u2502 ", line] }, index)) : (_jsx(MarkdownLine, { line: line }, index));
154
+ }) }));
155
+ }
156
+ export function Transcript({ items, activeText, activeThinking = '', thinkingExpanded = false, detailedTranscript = false, screenReader, }) {
157
+ const detailed = thinkingExpanded || detailedTranscript;
158
+ const results = new Map(items
159
+ .filter((item) => item.kind === 'tool-result')
160
+ .map((item) => [item.callId, item]));
161
+ const pairedResults = new Set(items
162
+ .filter((item) => item.kind === 'tool' && results.has(item.call.id))
163
+ .map((item) => item.call.id));
164
+ const shellResults = new Map(items
165
+ .filter((item) => item.kind === 'shell-result')
166
+ .map((item) => [item.callId, item]));
167
+ const pairedShellResults = new Set(items
168
+ .filter((item) => item.kind === 'shell' && shellResults.has(item.callId))
169
+ .map((item) => item.callId));
170
+ const readGroupStarts = new Map();
171
+ const groupedReadTools = new Set();
172
+ if (!detailed && !screenReader) {
173
+ for (let index = 0; index < items.length; index += 1) {
174
+ const first = items[index];
175
+ if (first?.kind !== 'tool' || first.call.name !== 'Read')
176
+ continue;
177
+ let cursor = index;
178
+ const memberIndexes = [];
179
+ const memberCallIds = new Set();
180
+ while (cursor < items.length) {
181
+ const candidate = items[cursor];
182
+ if (candidate?.kind === 'tool' && candidate.call.name === 'Read') {
183
+ const result = results.get(candidate.call.id);
184
+ if (!result || result.isError)
185
+ break;
186
+ memberIndexes.push(cursor);
187
+ memberCallIds.add(candidate.call.id);
188
+ cursor += 1;
189
+ continue;
190
+ }
191
+ if (candidate?.kind === 'tool-result' &&
192
+ memberCallIds.has(candidate.callId)) {
193
+ cursor += 1;
194
+ continue;
195
+ }
196
+ break;
197
+ }
198
+ if (memberIndexes.length === 0)
199
+ continue;
200
+ readGroupStarts.set(index, memberIndexes.length);
201
+ for (const memberIndex of memberIndexes.slice(1)) {
202
+ groupedReadTools.add(memberIndex);
203
+ }
204
+ index = cursor - 1;
205
+ }
206
+ }
207
+ return (_jsxs(Box, { flexDirection: "column", children: [items.map((item, index) => {
208
+ const readCount = readGroupStarts.get(index);
209
+ if (readCount !== undefined) {
210
+ return (_jsxs(Text, { children: [' ', "Read ", readCount, " file", readCount === 1 ? '' : 's', ' ', _jsx(Text, { dimColor: true, children: "(ctrl+o to expand)" })] }, index));
211
+ }
212
+ if (groupedReadTools.has(index))
213
+ return null;
214
+ if (item.kind === 'user') {
215
+ return (_jsxs(Box, { marginTop: index === 0 ? 0 : 1, children: [_jsx(Text, { color: BRAND, bold: true, children: screenReader ? 'You: ' : '❯ ' }), _jsx(Text, { bold: true, children: item.text })] }, index));
216
+ }
217
+ if (item.kind === 'assistant') {
218
+ return (_jsxs(Box, { marginTop: 1, children: [screenReader ? _jsx(Text, { children: "Praxis:" }) : null, !screenReader ? _jsx(Text, { color: ACCENT, children: "\u23FA " }) : null, _jsx(MarkdownText, { text: item.text })] }, index));
219
+ }
220
+ if (item.kind === 'thinking') {
221
+ return (_jsx(ThinkingBlock, { text: item.text, active: false, expanded: detailed, screenReader: screenReader }, index));
222
+ }
223
+ if (item.kind === 'compact') {
224
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: ACCENT, italic: true, children: screenReader
225
+ ? 'Conversation compacted'
226
+ : '✻ Conversation compacted (ctrl+o for history)' }), detailed ? (_jsx(Box, { marginLeft: screenReader ? 0 : 2, children: _jsx(MarkdownText, { text: item.summary }) })) : null] }, index));
227
+ }
228
+ if (item.kind === 'context') {
229
+ return (_jsx(ContextUsageBlock, { ...item, screenReader: screenReader }, index));
230
+ }
231
+ if (item.kind === 'tool') {
232
+ const result = results.get(item.call.id);
233
+ return (_jsx(ToolTranscriptEntry, { call: item.call, detail: item.detail, ...(result ? { result } : {}), detailed: detailed || screenReader }, index));
234
+ }
235
+ if (item.kind === 'tool-result') {
236
+ if (pairedResults.has(item.callId))
237
+ return null;
238
+ const text = item.text.length > 500 ? `${item.text.slice(0, 497)}...` : item.text;
239
+ return (_jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { color: item.isError ? 'red' : 'gray', children: item.isError ? '└ Error' : '└ Result' }), item.isError ? (_jsx(Text, { color: "red", children: text })) : (_jsx(ToolResultText, { text: text }))] }, index));
240
+ }
241
+ if (item.kind === 'shell') {
242
+ const result = shellResults.get(item.callId);
243
+ const output = result
244
+ ? [result.stdout, result.stderr]
245
+ .filter(Boolean)
246
+ .join(result.stdout &&
247
+ result.stderr &&
248
+ !result.stdout.endsWith('\n')
249
+ ? '\n'
250
+ : '')
251
+ : '';
252
+ const lines = contentLines(output);
253
+ const visible = detailed || screenReader ? lines : lines.slice(0, 3);
254
+ const hidden = lines.length - visible.length;
255
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { children: _jsxs(Text, { bold: true, children: [screenReader ? 'Shell command: ' : '! ', item.command] }) }), result ? (_jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [lines.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u23BF " })) : (visible.map((line, lineIndex) => (_jsxs(Text, { ...(result.isError ? { color: 'red' } : {}), dimColor: !result.isError, children: [lineIndex === 0 ? '⎿ ' : ' ', line || ' '] }, lineIndex)))), hidden > 0 ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2026 +", hidden, " lines (ctrl+o to expand)"] })) : null] })) : null] }, index));
256
+ }
257
+ if (item.kind === 'shell-result') {
258
+ if (pairedShellResults.has(item.callId))
259
+ return null;
260
+ const output = [item.stdout, item.stderr].filter(Boolean).join('\n');
261
+ return (_jsxs(Text, { ...(item.isError ? { color: 'red' } : {}), children: ["\u23BF ", output] }, index));
262
+ }
263
+ if (item.kind === 'local-result') {
264
+ return (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["\u23BF ", item.text] }) }, index));
265
+ }
266
+ return (_jsxs(Text, { ...(item.kind === 'warning' ? { color: 'red' } : {}), dimColor: item.kind === 'notice', children: [item.kind === 'warning' ? '⚠ ' : '· ', item.text] }, index));
267
+ }), activeThinking ? (_jsx(ThinkingBlock, { text: activeThinking, active: true, expanded: detailed, screenReader: screenReader })) : activeText ? (_jsxs(Box, { marginTop: 1, children: [screenReader ? (_jsx(Text, { children: "Praxis: " })) : (_jsx(Text, { color: ACCENT, children: "\u2733 " })), _jsx(MarkdownText, { text: activeText })] })) : null] }));
268
+ }
269
+ export function DiffDashboard({ snapshots, sourceIndex, selectedIndex, viewingFile, scrollOffset, width, screenReader, }) {
270
+ const source = snapshots[sourceIndex];
271
+ const snapshot = source?.snapshot ?? { files: [], additions: 0, deletions: 0 };
272
+ const selected = snapshot.files[selectedIndex];
273
+ const panelWidth = Math.max(32, Math.min(100, width));
274
+ const line = '─'.repeat(panelWidth);
275
+ if (viewingFile && selected) {
276
+ const lines = visiblePatchLines(selected.patch).slice(scrollOffset, scrollOffset + 18);
277
+ return (_jsxs(Box, { flexDirection: "column", children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsx(Text, { bold: true, children: " Uncommitted changes (git diff HEAD)" }), _jsxs(Text, { children: [' ', snapshots.map((item, index) => (_jsxs(Text, { inverse: index === sourceIndex, children: [' ', item.label, ' '] }, item.label)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { bold: true, children: [" ", selected.path] }), _jsxs(Text, { dimColor: true, children: [" ", '─'.repeat(Math.max(1, panelWidth - 4))] }), lines.map((patchLine, index) => (_jsxs(Text, { ...(patchLine.startsWith('+')
278
+ ? { color: 'green' }
279
+ : patchLine.startsWith('-')
280
+ ? { color: 'red' }
281
+ : { dimColor: true }), children: [' ', patchLine] }, `${scrollOffset}-${index}`))), _jsx(Text, { dimColor: true, children: " \u2191/\u2193 to scroll \u00B7 Esc to back" })] }));
282
+ }
283
+ return (_jsxs(Box, { flexDirection: "column", children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsx(Text, { bold: true, children: " Uncommitted changes (git diff HEAD)" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [' ', snapshots.map((item, index) => (_jsxs(Text, { inverse: index === sourceIndex, children: [' ', item.label, ' '] }, item.label)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [' ', snapshot.files.length, " file", snapshot.files.length === 1 ? '' : 's', ' ', "changed ", _jsxs(Text, { color: "green", children: ["+", snapshot.additions] }), ' ', _jsxs(Text, { color: "red", children: ["-", snapshot.deletions] })] }), _jsx(Text, { children: " " }), snapshot.files.length === 0 ? (_jsx(Text, { dimColor: true, children: " No uncommitted changes." })) : (snapshot.files.map((file, index) => (_jsxs(Text, { inverse: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', file.path, " ", _jsxs(Text, { color: "green", children: ["+", file.additions] }), ' ', _jsxs(Text, { color: "red", children: ["-", file.deletions] })] }, file.path)))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2190/\u2192 to switch source \u00B7 \u2191/\u2193 to select \u00B7 Enter to view \u00B7 Esc to close" })] }));
284
+ }
285
+ export function PermissionDashboard({ tabIndex, selectedIndex, query, rules, recentDenied, workspaceDirectories, width, screenReader, }) {
286
+ const tabs = ['Recently denied', 'Allow', 'Ask', 'Deny', 'Workspace'];
287
+ const behavior = ['allow', 'ask', 'deny'][tabIndex - 1];
288
+ const normalizedQuery = query.trim().toLowerCase();
289
+ const matchingRules = behavior
290
+ ? rules.filter((rule) => rule.behavior === behavior &&
291
+ (!normalizedQuery ||
292
+ rule.rule.toLowerCase().includes(normalizedQuery) ||
293
+ rule.scope.includes(normalizedQuery)))
294
+ : [];
295
+ const rows = tabIndex === 0 ? recentDenied : matchingRules.map((rule) => rule.rule);
296
+ const originalWorkspace = workspaceDirectories.find((directory) => directory.original);
297
+ const additionalWorkspaces = workspaceDirectories.filter((directory) => !directory.original);
298
+ const description = tabIndex === 0
299
+ ? recentDenied.length === 0
300
+ ? 'No recent denials. Commands denied by the auto mode classifier will appear here.'
301
+ : 'Commands denied by the auto mode classifier appear here.'
302
+ : tabIndex === 1
303
+ ? "Praxis Code won't ask before using allowed tools."
304
+ : tabIndex === 2
305
+ ? 'Praxis Code will always ask for confirmation before using these tools.'
306
+ : tabIndex === 3
307
+ ? 'Praxis Code will reject requests to use denied tools.'
308
+ : 'Praxis Code can read files in the workspace, and make edits when auto-accept edits is on.';
309
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(100, width)) })) : null, _jsx(Text, { bold: true, children: " Permissions" }), _jsxs(Text, { children: [' ', tabs.map((tab, index) => (_jsxs(Text, { inverse: index === tabIndex, children: [' ', tab, ' '] }, tab)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", description] }), tabIndex >= 1 && tabIndex <= 3 ? (_jsx(Box, { borderStyle: screenReader ? undefined : 'round', paddingX: 1, marginY: 1, children: _jsxs(Text, { ...(query ? {} : { dimColor: true }), children: ["\u2315 ", query || 'Search…'] }) })) : (_jsx(Text, { children: " " })), tabIndex >= 1 && tabIndex <= 3 ? (['Add a new rule…', ...rows].map((row, index) => (_jsxs(Text, { inverse: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', index + 1, ". ", row] }, `${index}-${row}`)))) : tabIndex === 4 ? (_jsxs(_Fragment, { children: [originalWorkspace ? (_jsxs(Text, { children: [' - ', originalWorkspace.path, " (Original working directory)"] })) : null, additionalWorkspaces.map((directory, index) => (_jsxs(Text, { inverse: selectedIndex === index, children: [selectedIndex === index ? '❯ ' : ' ', index + 1, ". ", directory.path] }, directory.path))), _jsxs(Text, { inverse: selectedIndex === additionalWorkspaces.length, children: [selectedIndex === additionalWorkspaces.length ? '❯ ' : ' ', additionalWorkspaces.length + 1, ". Add directory\u2026"] })] })) : rows.length > 0 ? (rows.map((row, index) => (_jsxs(Text, { inverse: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', index + 1, ". ", row] }, `${index}-${row}`)))) : null, _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: selectedIndex >= 0
310
+ ? '↑/↓ navigate · Enter to select · ←/→ to switch · Esc to cancel'
311
+ : '←/→ to switch · ↓ to select · Esc to cancel' })] }));
312
+ }
313
+ export function StatusDashboard({ tabIndex, version, sessionId, display, usage, costUsd, turnCount, toolCount, commandCount, detailedTranscript, width, screenReader, }) {
314
+ const tabs = ['Settings', 'Status', 'Config', 'Usage', 'Stats'];
315
+ const rows = tabIndex === 0
316
+ ? [
317
+ ['Thinking mode', 'provider controlled'],
318
+ ['Verbose output', detailedTranscript ? 'true' : 'false'],
319
+ ['Default permission mode', permissionLabel(display.permissionMode)],
320
+ ['Context compaction', 'automatic'],
321
+ ['Shared Claude data', 'enabled'],
322
+ ]
323
+ : tabIndex === 1
324
+ ? [
325
+ ['Version', version],
326
+ ['Session ID', sessionId ?? 'new session'],
327
+ ['cwd', display.cwd],
328
+ ['Model', display.model ?? 'provider default'],
329
+ ['Permission mode', permissionLabel(display.permissionMode)],
330
+ ]
331
+ : tabIndex === 2
332
+ ? [
333
+ ['Model', display.model ?? 'provider default'],
334
+ ['Effort', display.effort ?? 'high'],
335
+ [
336
+ 'Context window',
337
+ String(display.contextWindowTokens ?? 'provider default'),
338
+ ],
339
+ ['Available commands', String(commandCount)],
340
+ ]
341
+ : tabIndex === 3
342
+ ? [
343
+ ['Input tokens', String(usage?.inputTokens ?? 0)],
344
+ ['Output tokens', String(usage?.outputTokens ?? 0)],
345
+ [
346
+ 'Session cost',
347
+ costUsd === undefined
348
+ ? 'unavailable'
349
+ : `$${costUsd.toFixed(4)}`,
350
+ ],
351
+ ]
352
+ : [
353
+ ['Turns', String(turnCount)],
354
+ ['Tool calls', String(toolCount)],
355
+ [
356
+ 'Context used',
357
+ String((usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0)),
358
+ ],
359
+ ];
360
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(100, width)) })) : null, _jsxs(Text, { children: [' ', tabs.map((tab, index) => (_jsxs(Text, { inverse: index === tabIndex, children: [' ', tab, ' '] }, tab)))] }), _jsx(Text, { children: " " }), rows.map(([label, value]) => (_jsxs(Box, { children: [_jsx(Box, { width: 28, children: _jsxs(Text, { children: [label, ":"] }) }), _jsx(Text, { children: value })] }, label))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2190/\u2192/tab to switch \u00B7 Esc to close" })] }));
361
+ }
362
+ export function ListDashboard({ title, rows, emptyText, selectedIndex, width, screenReader, }) {
363
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(100, width)) })) : null, _jsxs(Text, { bold: true, children: [" ", title] }), _jsx(Text, { children: " " }), rows.length === 0 ? (_jsxs(Text, { dimColor: true, children: [" ", emptyText] })) : (rows.map((row, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { inverse: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', row.label] }), row.description ? _jsxs(Text, { dimColor: true, children: [" ", row.description] }) : null] }, `${index}-${row.label}`)))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191/\u2193 to select \u00B7 Esc to close" })] }));
364
+ }
365
+ export function SessionPicker({ sessions, selectedIndex, screenReader, query = '', }) {
366
+ const maxVisible = 8;
367
+ const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), sessions.length - maxVisible));
368
+ const visible = sessions.slice(start, start + maxVisible);
369
+ return (_jsxs(Box, { flexDirection: "column", children: [!screenReader ? _jsx(Text, { dimColor: true, children: '─'.repeat(80) }) : null, _jsx(Text, { bold: true, children: " Resume session" }), _jsx(Box, { borderStyle: screenReader ? undefined : 'round', borderColor: "gray", paddingX: screenReader ? 0 : 1, marginY: 1, children: _jsxs(Text, { ...(query ? {} : { dimColor: true }), children: ["\u2315 ", query || 'Search…'] }) }), start > 0 ? _jsxs(Text, { dimColor: true, children: [" \u2191 ", start, " earlier"] }) : null, visible.length === 0 ? _jsx(Text, { dimColor: true, children: "No sessions found." }) : null, visible.map((session, visibleIndex) => {
370
+ const index = start + visibleIndex;
371
+ return (_jsxs(Box, { children: [_jsxs(Text, { ...(index === selectedIndex ? { color: BRAND } : {}), bold: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', session
372
+ ? (session.name ?? session.lastPrompt ?? 'Untitled')
373
+ : 'Start a new session'] }), session ? (_jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", session.sessionId, " \u00B7 ", session.status] })) : null] }, session?.sessionId ?? 'new'));
374
+ }), start + visible.length < sessions.length ? (_jsxs(Text, { dimColor: true, children: [" \u2193 ", sessions.length - start - visible.length, " more"] })) : null, !screenReader ? (_jsx(Text, { dimColor: true, children: "\u2191/\u2193 to navigate \u00B7 Enter to select \u00B7 Type to search \u00B7 Esc to cancel" })) : null] }));
375
+ }
376
+ export function CommandPalette({ commands, selectedIndex, width, screenReader, }) {
377
+ const maxVisible = 12;
378
+ const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, commands.length - maxVisible)));
379
+ const visible = commands.slice(start, start + maxVisible);
380
+ if (screenReader) {
381
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Commands" }), visible.length === 0 ? (_jsx(Text, { children: "No matching commands." })) : (visible.map((command) => (_jsxs(Text, { children: ["/", command.name, ": ", command.description] }, command.name))))] }));
382
+ }
383
+ return (_jsx(Box, { flexDirection: "column", width: Math.min(100, width), children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No matching commands." })) : (visible.map((command, visibleIndex) => {
384
+ const index = start + visibleIndex;
385
+ const selected = index === selectedIndex;
386
+ return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 30, children: _jsxs(Text, { ...(selected ? { color: BRAND, bold: true } : {}), children: ["/", command.name] }) }), _jsx(Box, { flexGrow: 1, children: _jsx(Text, { dimColor: true, children: command.description }) })] }, command.name));
387
+ })) }));
388
+ }
389
+ export function FilePicker({ entries, selectedIndex, width, screenReader, }) {
390
+ const maxVisible = 12;
391
+ const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, entries.length - maxVisible)));
392
+ const visible = entries.slice(start, start + maxVisible);
393
+ return (_jsx(Box, { flexDirection: "column", width: Math.min(100, width), children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No matching files." })) : (visible.map((entry, visibleIndex) => {
394
+ const index = start + visibleIndex;
395
+ return (_jsxs(Text, { inverse: !screenReader && index === selectedIndex, children: [screenReader && index === selectedIndex ? 'Selected: ' : '+ ', entry.path] }, entry.path));
396
+ })) }));
397
+ }
398
+ export function MentionPicker({ entries, selectedIndex, width, screenReader, }) {
399
+ const maxVisible = 12;
400
+ const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, entries.length - maxVisible)));
401
+ const visible = entries.slice(start, start + maxVisible);
402
+ return (_jsx(Box, { flexDirection: "column", width: Math.min(100, width), children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No matching files or agents." })) : (visible.map((entry, visibleIndex) => {
403
+ const index = start + visibleIndex;
404
+ const selected = index === selectedIndex;
405
+ if (entry.kind === 'agent') {
406
+ return (_jsxs(Text, { inverse: !screenReader && selected, ...(screenReader ? {} : { wrap: 'truncate-end' }), children: [screenReader && selected ? 'Selected agent: ' : '* ', entry.name, screenReader ? '' : ' (agent)', entry.description ? ` – ${entry.description}` : ''] }, `agent:${entry.name}`));
407
+ }
408
+ return (_jsxs(Text, { inverse: !screenReader && selected, ...(screenReader ? {} : { wrap: 'truncate-end' }), children: [screenReader && selected ? 'Selected: ' : '+ ', entry.path] }, `file:${entry.path}`));
409
+ })) }));
410
+ }
411
+ const SHORTCUT_ROWS = [
412
+ [
413
+ '! for shell mode',
414
+ 'double tap esc to clear input',
415
+ 'ctrl + shift + _ to undo',
416
+ ],
417
+ ['/ for commands', 'shift + tab to cycle permissions', 'ctrl + z to suspend'],
418
+ [
419
+ '@ for file paths',
420
+ 'ctrl + o for verbose output',
421
+ 'ctrl + v to paste images',
422
+ ],
423
+ [
424
+ '/btw for side question',
425
+ 'ctrl + t to toggle tasks',
426
+ 'opt + p to switch model',
427
+ ],
428
+ ['', 'backslash (\\) + return (⏎) for newline', 'ctrl + s to stash prompt'],
429
+ ['', '', 'ctrl + g to edit in $EDITOR'],
430
+ ['', '', '/keybindings to customize'],
431
+ ];
432
+ export function ShortcutHelp({ width }) {
433
+ const wide = width >= 78;
434
+ return (_jsx(Box, { flexDirection: "column", width: Math.min(100, width), children: SHORTCUT_ROWS.map((row, rowIndex) => wide ? (_jsx(Box, { flexDirection: "row", children: row.map((cell, cellIndex) => (_jsx(Box, { width: cellIndex === 2 ? undefined : '33%', children: _jsx(Text, { dimColor: true, children: cell }) }, cellIndex))) }, rowIndex)) : (row.filter(Boolean).map((cell, cellIndex) => (_jsx(Text, { dimColor: true, children: cell }, `${rowIndex}-${cellIndex}`))))) }));
435
+ }
436
+ export function HelpMenu({ tabIndex, selectedIndex, builtinCommands, customCommands, width, screenReader, }) {
437
+ const tabs = ['General', 'Commands', 'Custom commands'];
438
+ const commands = tabIndex === 1 ? builtinCommands : customCommands;
439
+ const maxVisible = 10;
440
+ const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, commands.length - maxVisible)));
441
+ const visible = commands.slice(start, start + maxVisible);
442
+ const line = '─'.repeat(Math.max(12, Math.min(100, width)));
443
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsxs(Box, { children: [_jsx(Text, { bold: true, children: " Help " }), tabs.map((tab, index) => (_jsxs(Text, { ...(index === tabIndex ? { color: BRAND, bold: true } : {}), children: [' ', tab, ' '] }, tab)))] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: tabIndex === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: "Praxis understands your codebase, makes edits with your permission, and executes commands from your terminal." }), _jsx(Text, { bold: true, children: "Shortcuts" }), _jsx(ShortcutHelp, { width: width })] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: tabIndex === 1
444
+ ? 'Browse default commands'
445
+ : 'Browse shared commands and skills' }), visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No commands found." })) : (visible.map((command, visibleIndex) => {
446
+ const index = start + visibleIndex;
447
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { ...(index === selectedIndex
448
+ ? { color: BRAND, bold: true }
449
+ : {}), children: [index === selectedIndex ? '↓ ' : ' ', "/", command.name] }), _jsxs(Text, { dimColor: true, children: [" ", command.description] })] }, command.name));
450
+ }))] })) }), _jsx(Text, { dimColor: true, children: "\u2190/\u2192 to switch \u00B7 \u2191/\u2193 to navigate \u00B7 Esc to cancel" })] }));
451
+ }
452
+ export function SelectionMenu({ title, description, options, selectedIndex, footer, width, screenReader, }) {
453
+ const maxVisible = 7;
454
+ const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, options.length - maxVisible)));
455
+ const visible = options.slice(start, start + maxVisible);
456
+ return (_jsxs(Box, { borderStyle: screenReader ? undefined : 'round', borderColor: "gray", flexDirection: "column", marginTop: 1, paddingX: screenReader ? 0 : 1, width: screenReader ? undefined : Math.min(80, width), children: [_jsx(Text, { bold: true, children: title }), description ? _jsx(Text, { dimColor: true, children: description }) : null, visible.map((option, visibleIndex) => {
457
+ const index = start + visibleIndex;
458
+ const selected = index === selectedIndex;
459
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { ...(selected ? { color: BRAND, bold: true } : {}), children: [selected ? '❯ ' : ' ', index + 1, ". ", option.label, option.selected ? ' ✔' : ''] }), option.description ? (_jsxs(Text, { dimColor: true, children: [" ", option.description] })) : null] }, `${index}-${option.label}`));
460
+ }), start > 0 || start + visible.length < options.length ? (_jsxs(Text, { dimColor: true, children: [start > 0 ? `↑ ${start} earlier` : '', start > 0 && start + visible.length < options.length ? ' · ' : '', start + visible.length < options.length
461
+ ? `↓ ${options.length - start - visible.length} more`
462
+ : ''] })) : null, _jsx(Text, { dimColor: true, children: footer })] }));
463
+ }
464
+ function ComposerInput({ input, cursor }) {
465
+ const { before, current, after } = composerEditorSegments({
466
+ text: input,
467
+ cursor,
468
+ });
469
+ const cursorText = current === '\n' ? '↵' : (current ?? ' ');
470
+ return (_jsxs(Text, { children: [before, _jsx(Text, { color: "black", backgroundColor: ACCENT, children: cursorText }), after] }));
471
+ }
472
+ export function ExternalEditorWait({ screenReader, }) {
473
+ if (screenReader)
474
+ return _jsx(Text, { children: "External editor open. Save and close it to continue." });
475
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Text, { children: "Save and close editor to continue..." }), _jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" })] }));
476
+ }
477
+ export function Composer({ input, cursor, busy, clipboardBusy = false, status, display, usage, costUsd, width, screenReader, hasThinking = false, thinkingExpanded = false, shortcutsVisible = false, shellMode = false, footerMessage, }) {
478
+ const [spinnerIndex, setSpinnerIndex] = useState(0);
479
+ useEffect(() => {
480
+ if (!busy || screenReader)
481
+ return;
482
+ const timer = setInterval(() => setSpinnerIndex((current) => (current + 1) % SPINNER.length), 90);
483
+ return () => clearInterval(timer);
484
+ }, [busy, screenReader]);
485
+ if (screenReader)
486
+ return (_jsx(Text, { children: clipboardBusy
487
+ ? 'Pasting…'
488
+ : busy
489
+ ? `Status: ${status}`
490
+ : shellMode
491
+ ? `Shell command: ${input}`
492
+ : `Prompt: ${input}` }));
493
+ const line = '─'.repeat(Math.max(12, Math.min(100, width)));
494
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [usage ? (_jsxs(Text, { dimColor: true, children: ["Context \u00B7 ", usage.inputTokens + usage.outputTokens, " tokens", display.contextWindowTokens
495
+ ? ` / ${display.contextWindowTokens} (${Math.min(100, Math.round(((usage.inputTokens + usage.outputTokens) /
496
+ display.contextWindowTokens) *
497
+ 100))}%)`
498
+ : '', usage.cacheReadInputTokens
499
+ ? ` · ${usage.cacheReadInputTokens} cached`
500
+ : '', costUsd === undefined ? '' : ` · $${costUsd.toFixed(6)}`] })) : null, _jsx(Text, { dimColor: true, children: line }), clipboardBusy ? (_jsx(Text, { children: "Pasting\u2026" })) : busy ? (_jsxs(Text, { children: [_jsx(Text, { color: ACCENT, children: SPINNER[spinnerIndex] }), " ", status, "\u2026", ' ', _jsx(Text, { dimColor: true, children: "\u00B7 esc to interrupt" })] })) : (_jsxs(Text, { children: [_jsx(Text, { ...(shellMode ? {} : { color: BRAND }), bold: true, children: shellMode ? '! ' : '❯ ' }), input ? (_jsx(ComposerInput, { cursor: cursor ?? Array.from(input).length, input: input })) : (_jsx(Text, { dimColor: true, children: shellMode
501
+ ? 'Enter a shell command'
502
+ : 'Try "review this project"' }))] })), _jsx(Text, { dimColor: true, children: line }), shortcutsVisible ? (_jsx(ShortcutHelp, { width: width })) : (_jsxs(Box, { width: Math.min(100, width), children: [_jsx(Text, { dimColor: true, children: shellMode ? ('! for shell mode') : (_jsxs(_Fragment, { children: ["\u23F5\u23F5 ", permissionLabel(display.permissionMode), " \u00B7", ' ', busy ? 'esc to interrupt' : '? for shortcuts', " \u00B7 \u2190 for agents", hasThinking
503
+ ? ` · ctrl+o ${thinkingExpanded ? 'collapse' : 'expand'}`
504
+ : ''] })) }), _jsx(Box, { flexGrow: 1 }), footerMessage ? (footerMessage.isError ? (_jsx(Text, { color: "red", children: footerMessage.text })) : (_jsx(Text, { dimColor: true, children: footerMessage.text }))) : display.effort ? (_jsxs(Text, { children: [_jsxs(Text, { color: ACCENT, children: ["\u25CF ", display.effort] }), _jsx(Text, { dimColor: true, children: " \u00B7 /effort" })] })) : null] }))] }));
505
+ }
506
+ export function DialogFrame({ title, children, screenReader, }) {
507
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: screenReader ? undefined : 'round', borderColor: "yellow", paddingX: screenReader ? 0 : 1, marginTop: 1, children: [_jsx(Text, { color: "yellow", bold: true, children: title }), children] }));
508
+ }
509
+ //# sourceMappingURL=claude-style.js.map