praxis-agent 0.9.0 → 0.11.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.
- package/README.md +18 -4
- package/dist/application/session-service.d.ts +5 -2
- package/dist/application/session-service.js +23 -0
- package/dist/application/subagent-service.d.ts +4 -2
- package/dist/application/subagent-service.js +4 -0
- package/dist/cli/interactive.d.ts +4 -3
- package/dist/cli/interactive.js +760 -195
- package/dist/cli/tui/claude-command-inventory.d.ts +368 -0
- package/dist/cli/tui/claude-command-inventory.js +220 -0
- package/dist/cli/tui/claude-style.d.ts +12 -14
- package/dist/cli/tui/claude-style.js +45 -67
- package/dist/cli/tui/config-dashboard.d.ts +4 -8
- package/dist/cli/tui/config-dashboard.js +4 -18
- package/dist/cli/tui/config-settings.js +1 -1
- package/dist/cli/tui/conversation-export.d.ts +1 -1
- package/dist/cli/tui/conversation-export.js +2 -11
- package/dist/cli/tui/mcp-elicitation.d.ts +75 -0
- package/dist/cli/tui/mcp-elicitation.js +497 -0
- package/dist/cli/tui/open-url.d.ts +2 -0
- package/dist/cli/tui/open-url.js +13 -0
- package/dist/cli/tui/permission-settings.d.ts +6 -0
- package/dist/cli/tui/permission-settings.js +84 -0
- package/dist/cli/tui/runtime-interactions.d.ts +2 -0
- package/dist/cli/tui/runtime-interactions.js +44 -17
- package/dist/cli/tui/slash-commands.js +3 -28
- package/dist/cli/tui/tool-permission.d.ts +36 -0
- package/dist/cli/tui/tool-permission.js +386 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +28 -13
- package/dist/core/runtime.d.ts +50 -2
- package/dist/core/runtime.js +30 -3
- package/dist/extensions/claude-extension-tools.d.ts +3 -2
- package/dist/extensions/claude-extension-tools.js +15 -5
- package/dist/extensions/claude-extensions.d.ts +1 -0
- package/dist/extensions/claude-extensions.js +10 -0
- package/dist/hooks/claude-hook-tools.js +1 -1
- package/dist/mcp/claude-mcp-tools.js +12 -6
- package/dist/permissions/bash-ast.d.ts +39 -0
- package/dist/permissions/bash-ast.js +1046 -0
- package/dist/permissions/bash-normalization.d.ts +5 -0
- package/dist/permissions/bash-normalization.js +241 -0
- package/dist/permissions/bash-path-safety.d.ts +28 -0
- package/dist/permissions/bash-path-safety.js +575 -0
- package/dist/permissions/claude-permission-resolver.d.ts +7 -0
- package/dist/permissions/claude-permission-resolver.js +351 -40
- package/dist/permissions/claude-shell-permission.d.ts +4 -0
- package/dist/permissions/claude-shell-permission.js +108 -0
- package/dist/permissions/permission-updates.d.ts +16 -0
- package/dist/permissions/permission-updates.js +320 -0
- package/dist/permissions/sed-safety.d.ts +9 -0
- package/dist/permissions/sed-safety.js +172 -0
- package/dist/permissions/shell-rule-matching.d.ts +14 -0
- package/dist/permissions/shell-rule-matching.js +76 -0
- package/dist/tools/claude-interactive-tools.d.ts +10 -1
- package/dist/tools/claude-interactive-tools.js +10 -4
- package/dist/tools/local-tools.js +22 -2
- package/package.json +3 -1
|
@@ -46,8 +46,8 @@ function permissionLabel(mode) {
|
|
|
46
46
|
}
|
|
47
47
|
function selectionPrefix(selected, screenReader) {
|
|
48
48
|
if (selected)
|
|
49
|
-
return screenReader ? 'Selected: ' : '❯ ';
|
|
50
|
-
return screenReader ? '' : '
|
|
49
|
+
return screenReader ? 'Selected: ' : ' ❯ ';
|
|
50
|
+
return screenReader ? '' : ' ';
|
|
51
51
|
}
|
|
52
52
|
export function WelcomePanel({ display, width, }) {
|
|
53
53
|
const palette = useTuiPalette();
|
|
@@ -59,7 +59,7 @@ export function WelcomePanel({ display, width, }) {
|
|
|
59
59
|
// Title block = "╭" + "───" + "Praxis Code vX.Y.Z" + " " = 4 + title.length + 1 chars.
|
|
60
60
|
const title = `${brand} Code v${display.version}`;
|
|
61
61
|
const fill = Math.max(1, panelWidth - title.length - 6);
|
|
62
|
-
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) }), '╮'] }), _jsx(Box, { borderStyle: "round", borderColor: palette.muted, borderTop: false, flexDirection: "column", width: panelWidth, paddingX: 1, children: _jsxs(Box, { flexDirection: wide ? 'row' : 'column', children: [_jsxs(Box, { alignItems: wide ? 'center' : undefined, flexDirection: "column", width: wide ? '50%' : '100%', children: [_jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Welcome back!" }), _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, { 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 /
|
|
62
|
+
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) }), '╮'] }), _jsx(Box, { borderStyle: "round", borderColor: palette.muted, borderTop: false, flexDirection: "column", width: panelWidth, paddingX: 1, children: _jsxs(Box, { flexDirection: wide ? 'row' : 'column', children: [_jsxs(Box, { alignItems: wide ? 'center' : undefined, flexDirection: "column", width: wide ? '50%' : '100%', children: [_jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Welcome back!" }), _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, { 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 /init to create a CLAUDE.md file with instructions for Claude" }), _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: "What's new" }), _jsx(Text, { children: 'Subagent forking is now on by default: a `subagent_type: "fork"` subagent inherits the full conversation and prompt cache, and non-teammate agent spawns in interactive sessions now run in the background by default' }), _jsx(Text, { children: 'Type `@` in the prompt to mention another Claude session by name; Claude then uses `SendMessage` to reach that session directly' }), _jsx(Text, { children: '`SendMessage` now delivers to a bare name that exactly matches one live session, instead of asking to confirm with a ref first' }), _jsx(Text, { dimColor: true, children: "/release-notes for more" })] })] }) })] }));
|
|
63
63
|
}
|
|
64
64
|
function InlineText({ text }) {
|
|
65
65
|
const palette = useTuiPalette();
|
|
@@ -183,19 +183,27 @@ function ThinkingBlock({ text, active, expanded, screenReader, }) {
|
|
|
183
183
|
const showFull = screenReader || active || expanded;
|
|
184
184
|
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { ...(!screenReader ? { color: palette.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] }));
|
|
185
185
|
}
|
|
186
|
-
function
|
|
186
|
+
function compactTokens(tokens) {
|
|
187
|
+
if (tokens >= 1_000_000)
|
|
188
|
+
return `${(tokens / 1_000_000).toFixed(1).replace(/\.0$/u, '')}m`;
|
|
189
|
+
if (tokens >= 1_000)
|
|
190
|
+
return `${(tokens / 1_000).toFixed(1).replace(/\.0$/u, '')}k`;
|
|
191
|
+
return String(tokens);
|
|
192
|
+
}
|
|
193
|
+
function percent(tokens, total) {
|
|
194
|
+
return `${Math.round((tokens / Math.max(1, total)) * 100 * 10) / 10}%`;
|
|
195
|
+
}
|
|
196
|
+
function ContextUsageBlock({ usedTokens, contextWindowTokens, model, skills, memoryFiles, screenReader, }) {
|
|
187
197
|
const totalTokens = Math.max(1, contextWindowTokens);
|
|
188
198
|
const compactBuffer = Math.round(totalTokens * 0.165);
|
|
189
199
|
const usable = Math.max(1, totalTokens - compactBuffer);
|
|
190
200
|
if (screenReader) {
|
|
191
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Context Usage" }), _jsxs(Text, { children: [usedTokens.toLocaleString(), "
|
|
201
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Context Usage" }), _jsxs(Text, { children: [model ?? 'provider default', " \u00B7 ", usedTokens.toLocaleString(), "/", totalTokens.toLocaleString(), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsxs(Text, { children: ["Autocompact buffer: ", compactBuffer.toLocaleString(), " tokens"] }), _jsxs(Text, { children: ["Skills: ", skills.map(({ name }) => name).join(', ') || 'none'] })] }));
|
|
192
202
|
}
|
|
193
|
-
const usedCells = Math.min(
|
|
194
|
-
const bufferCells = Math.min(
|
|
195
|
-
const cells = Array.from({ length:
|
|
196
|
-
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:
|
|
197
|
-
.reduce((total, skill) => total + skill.tokens, 0)
|
|
198
|
-
.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))))] }));
|
|
203
|
+
const usedCells = Math.min(25, Math.round((usedTokens / totalTokens) * 25));
|
|
204
|
+
const bufferCells = Math.min(25 - usedCells, Math.round((compactBuffer / totalTokens) * 25));
|
|
205
|
+
const cells = Array.from({ length: 25 }, (_, index) => index < usedCells ? '⛁' : index >= 25 - bufferCells ? '⛝' : '⛶');
|
|
206
|
+
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: 5 }, (_, row) => (_jsx(Text, { children: cells.slice(row * 5, row * 5 + 5).join(' ') }, row))) }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [model ?? 'provider default', " \u00B7 ", compactTokens(usedTokens), "/", compactTokens(totalTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, italic: true, children: "Estimated usage by category" }), _jsxs(Text, { children: ["\u26C1 Messages and other context: ", compactTokens(usedTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsxs(Text, { children: ["\u26F6 Free space: ", compactTokens(Math.max(0, usable - usedTokens)), " (", percent(Math.max(0, usable - usedTokens), totalTokens), ")"] }), _jsxs(Text, { children: ["\u26DD Autocompact buffer: ", compactTokens(compactBuffer), " tokens (", percent(compactBuffer, totalTokens), ")"] })] })] }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Memory files \u00B7 /memory" }), memoryFiles.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u2514 No memory files" })) : (memoryFiles.map((file, index) => (_jsxs(Text, { dimColor: true, children: [index === memoryFiles.length - 1 ? '└' : '├', " ", file.path, ":", ' ', file.tokens, " tokens"] }, file.path)))), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Skills \u00B7 /skills" }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Loaded" }), 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))))] }));
|
|
199
207
|
}
|
|
200
208
|
export function MarkdownText({ text }) {
|
|
201
209
|
const lines = text.split('\n');
|
|
@@ -403,7 +411,7 @@ export function ThemePicker({ currentTheme, selectedIndex, syntaxHighlightingDis
|
|
|
403
411
|
? {}
|
|
404
412
|
: { backgroundColor: syntax.addedBackground }), children: [' console.', _jsx(Text, { ...syntaxColor(syntax.identifier), children: "log" }), '(', _jsx(Text, { ...syntaxColor(syntax.string), children: "\"Hello, " }), _jsx(Text, { ...(syntaxHighlightingDisabled || !syntax.addedHighlight
|
|
405
413
|
? {}
|
|
406
|
-
: { backgroundColor: syntax.addedHighlight }), children: "
|
|
414
|
+
: { backgroundColor: syntax.addedHighlight }), children: "Claude" }), _jsx(Text, { ...syntaxColor(syntax.string), children: "!\"" }), '); '] })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " 3 " }), _jsx(Text, { ...syntaxColor(syntax.text), children: '}' })] }), _jsxs(Text, { dimColor: true, children: [' ', '╌'.repeat(Math.max(1, Math.min(96, width - 3)))] })] })) : null, _jsxs(Text, { dimColor: true, children: [' ', selected?.theme === '__new__'
|
|
407
415
|
? 'Enter to create a custom theme'
|
|
408
416
|
: syntaxHighlightingDisabled
|
|
409
417
|
? 'Syntax highlighting disabled (ctrl+t to enable)'
|
|
@@ -414,55 +422,6 @@ export function CustomThemeEditor({ theme, token, value, tokens = [], selectedIn
|
|
|
414
422
|
.slice(selectedIndex, selectedIndex + 8)
|
|
415
423
|
.map((entry, index) => (_jsxs(Text, { inverse: index === 0, children: [index === 0 ? '❯ ' : ' ', "\u2588\u2588 ", entry, theme.overrides[entry] === undefined ? '' : ' (custom)'] }, entry))), _jsx(Text, { dimColor: true, children: "\u2191/\u2193 to nav \u00B7 Enter to edit \u00B7 Tab to reset \u00B7 Esc to done" })] })), screenReader ? _jsx(Text, { children: "Editing custom theme token" }) : null] }));
|
|
416
424
|
}
|
|
417
|
-
export function StatusDashboard({ tabIndex, version, sessionId, display, usage, costUsd, turnCount, toolCount, commandCount, detailedTranscript, width, screenReader, }) {
|
|
418
|
-
const tabs = ['Settings', 'Status', 'Config', 'Usage', 'Stats'];
|
|
419
|
-
const rows = tabIndex === 0
|
|
420
|
-
? [
|
|
421
|
-
['Thinking mode', 'provider controlled'],
|
|
422
|
-
['Verbose output', detailedTranscript ? 'true' : 'false'],
|
|
423
|
-
['Default permission mode', permissionLabel(display.permissionMode)],
|
|
424
|
-
['Context compaction', 'automatic'],
|
|
425
|
-
['Shared Claude data', 'enabled'],
|
|
426
|
-
]
|
|
427
|
-
: tabIndex === 1
|
|
428
|
-
? [
|
|
429
|
-
['Version', version],
|
|
430
|
-
['Session ID', sessionId ?? 'new session'],
|
|
431
|
-
['cwd', display.cwd],
|
|
432
|
-
['Model', display.model ?? 'provider default'],
|
|
433
|
-
['Permission mode', permissionLabel(display.permissionMode)],
|
|
434
|
-
]
|
|
435
|
-
: tabIndex === 2
|
|
436
|
-
? [
|
|
437
|
-
['Model', display.model ?? 'provider default'],
|
|
438
|
-
['Effort', display.effort ?? 'high'],
|
|
439
|
-
[
|
|
440
|
-
'Context window',
|
|
441
|
-
String(display.contextWindowTokens ?? 'provider default'),
|
|
442
|
-
],
|
|
443
|
-
['Available commands', String(commandCount)],
|
|
444
|
-
]
|
|
445
|
-
: tabIndex === 3
|
|
446
|
-
? [
|
|
447
|
-
['Input tokens', String(usage?.inputTokens ?? 0)],
|
|
448
|
-
['Output tokens', String(usage?.outputTokens ?? 0)],
|
|
449
|
-
[
|
|
450
|
-
'Session cost',
|
|
451
|
-
costUsd === undefined
|
|
452
|
-
? 'unavailable'
|
|
453
|
-
: `$${costUsd.toFixed(4)}`,
|
|
454
|
-
],
|
|
455
|
-
]
|
|
456
|
-
: [
|
|
457
|
-
['Turns', String(turnCount)],
|
|
458
|
-
['Tool calls', String(toolCount)],
|
|
459
|
-
[
|
|
460
|
-
'Context used',
|
|
461
|
-
String((usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0)),
|
|
462
|
-
],
|
|
463
|
-
];
|
|
464
|
-
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" })] }));
|
|
465
|
-
}
|
|
466
425
|
export function ListDashboard({ title, rows, emptyText, selectedIndex, width, screenReader, }) {
|
|
467
426
|
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" })] }));
|
|
468
427
|
}
|
|
@@ -609,16 +568,17 @@ export function MentionPicker({ entries, selectedIndex, width, screenReader, })
|
|
|
609
568
|
}
|
|
610
569
|
const SHORTCUT_ROWS = [
|
|
611
570
|
[
|
|
612
|
-
'! for
|
|
571
|
+
'! for bash mode',
|
|
613
572
|
'double tap esc to clear input',
|
|
614
573
|
'ctrl + shift + _ to undo',
|
|
615
574
|
],
|
|
616
|
-
['/ for commands', 'shift + tab to
|
|
575
|
+
['/ for commands', 'shift + tab to auto-accept edits', 'ctrl + z to suspend'],
|
|
617
576
|
[
|
|
618
577
|
'@ for file paths',
|
|
619
578
|
'ctrl + o for verbose output',
|
|
620
579
|
'ctrl + v to paste images',
|
|
621
580
|
],
|
|
581
|
+
['& for background', '', ''],
|
|
622
582
|
[
|
|
623
583
|
'/btw for side question',
|
|
624
584
|
'ctrl + t to toggle tasks',
|
|
@@ -657,16 +617,34 @@ export function SelectionMenu({ title, description, options, selectedIndex, foot
|
|
|
657
617
|
const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, options.length - maxVisible)));
|
|
658
618
|
const visible = options.slice(start, start + maxVisible);
|
|
659
619
|
const current = options.find((option) => option.selected);
|
|
620
|
+
const maxLabelWidth = options.reduce((max, option, index) => Math.max(max, `${index + 1}. ${option.label}`.length), 0);
|
|
660
621
|
return (_jsxs(Box, { borderStyle: screenReader ? undefined : 'round', borderColor: palette.muted, 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, screenReader && current ? _jsxs(Text, { children: ["Current: ", current.label] }) : null, visible.map((option, visibleIndex) => {
|
|
661
622
|
const index = start + visibleIndex;
|
|
662
623
|
const selected = index === selectedIndex;
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
624
|
+
const rowLabel = `${index + 1}. ${option.label}`;
|
|
625
|
+
return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsxs(Text, { ...(!screenReader && selected
|
|
626
|
+
? { color: palette.brand, bold: true }
|
|
627
|
+
: {}), children: [selectionPrefix(selected, screenReader), rowLabel, !screenReader && option.selected ? ' ✔' : '', option.description ? (_jsxs(Text, { dimColor: true, children: [' '.repeat(Math.max(2, maxLabelWidth -
|
|
628
|
+
rowLabel.length -
|
|
629
|
+
(option.selected && !screenReader ? 2 : 0))), option.description] })) : null] }) }, `${index}-${option.label}`));
|
|
666
630
|
}), 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
|
|
667
631
|
? `↓ ${options.length - start - visible.length} more`
|
|
668
632
|
: ''] })) : null, _jsx(Text, { dimColor: true, children: footer })] }));
|
|
669
633
|
}
|
|
634
|
+
export function ModelMenu({ options, effort, selectedIndex, width, screenReader, }) {
|
|
635
|
+
const palette = useTuiPalette();
|
|
636
|
+
const current = options.find((option) => option.selected);
|
|
637
|
+
const maxLabelWidth = options.reduce((max, option, index) => Math.max(max, `${index + 1}. ${option.label}`.length), 0);
|
|
638
|
+
return (_jsxs(Box, { borderStyle: screenReader ? undefined : 'round', borderColor: palette.muted, flexDirection: "column", marginTop: 1, paddingX: screenReader ? 0 : 1, width: screenReader ? undefined : Math.min(80, width), children: [_jsx(Text, { bold: true, children: "Select model" }), _jsx(Text, { dimColor: true, children: 'Switch between models. Your pick applies to this and future Praxis Code sessions. For other model names, specify with --model.' }), screenReader && current ? _jsxs(Text, { children: ["Current: ", current.label] }) : null, options.map((option, index) => {
|
|
639
|
+
const selected = index === selectedIndex;
|
|
640
|
+
const rowLabel = `${index + 1}. ${option.label}`;
|
|
641
|
+
return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsxs(Text, { ...(!screenReader && selected
|
|
642
|
+
? { color: palette.brand, bold: true }
|
|
643
|
+
: {}), children: [selectionPrefix(selected, screenReader), rowLabel, !screenReader && option.selected ? ' ✔' : '', option.description ? (_jsxs(Text, { dimColor: true, children: [' '.repeat(Math.max(2, maxLabelWidth -
|
|
644
|
+
rowLabel.length -
|
|
645
|
+
(option.selected && !screenReader ? 2 : 0))), option.description] })) : null] }) }, `${index}-${option.label}`));
|
|
646
|
+
}), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: palette.accent, children: "\u25CF " }), effort.charAt(0).toUpperCase() + effort.slice(1), " effort (default)", _jsx(Text, { dimColor: true, children: " \u2190/\u2192 to adjust" })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter to select \u00B7 Esc to cancel" }) })] }));
|
|
647
|
+
}
|
|
670
648
|
function ComposerInput({ input, cursor }) {
|
|
671
649
|
const palette = useTuiPalette();
|
|
672
650
|
const { before, current, after } = composerEditorSegments({
|
|
@@ -707,7 +685,7 @@ export function Composer({ input, cursor, busy, clipboardBusy = false, status, d
|
|
|
707
685
|
? ` · ${usage.cacheReadInputTokens} cached`
|
|
708
686
|
: '', costUsd === undefined ? '' : ` · $${costUsd.toFixed(6)}`] })) : null, _jsx(Text, { dimColor: true, children: line }), clipboardBusy ? (_jsx(Text, { children: "Pasting\u2026" })) : busy ? (_jsxs(Text, { children: [progressBar ? (_jsx(Text, { color: palette.accent, children: reduceMotion ? '•' : SPINNER[spinnerIndex] })) : null, ' ', status, "\u2026 ", _jsx(Text, { dimColor: true, children: "\u00B7 esc to interrupt" })] })) : (_jsxs(Text, { children: [_jsx(Text, { ...(shellMode ? {} : { color: palette.brand }), bold: true, children: shellMode ? '! ' : '❯ ' }), input ? (_jsx(ComposerInput, { cursor: cursor ?? Array.from(input).length, input: input })) : (_jsx(Text, { dimColor: true, children: shellMode
|
|
709
687
|
? 'Enter a shell command'
|
|
710
|
-
: '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
|
|
688
|
+
: '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 bash mode') : (_jsxs(_Fragment, { children: ["\u23F5\u23F5 ", permissionLabel(display.permissionMode), " \u00B7", ' ', busy ? 'esc to interrupt' : '? for shortcuts', " \u00B7 \u2190 for agents", hasThinking
|
|
711
689
|
? ` · ctrl+o ${thinkingExpanded ? 'collapse' : 'expand'}`
|
|
712
690
|
: ''] })) }), _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] }))] }));
|
|
713
691
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ModelUsage } from '../../core/runtime.js';
|
|
3
3
|
import { type ConfigSettingDefinition, type ConfigSettingsSnapshot, type ConfigValue } from './config-settings.js';
|
|
4
|
-
export type ConfigDashboardTab = '
|
|
4
|
+
export type ConfigDashboardTab = 'status' | 'config' | 'usage';
|
|
5
5
|
export interface ConfigStatusData {
|
|
6
6
|
version: string;
|
|
7
7
|
sessionName?: string;
|
|
@@ -9,6 +9,8 @@ export interface ConfigStatusData {
|
|
|
9
9
|
cwd: string;
|
|
10
10
|
authSource?: string;
|
|
11
11
|
baseUrl?: string;
|
|
12
|
+
proxy?: string;
|
|
13
|
+
mcpSummary?: string;
|
|
12
14
|
model: string;
|
|
13
15
|
settingSources: readonly string[];
|
|
14
16
|
}
|
|
@@ -28,10 +30,6 @@ export interface ConfigContextData {
|
|
|
28
30
|
contextWindowTokens: number;
|
|
29
31
|
categories: readonly ContextCategory[];
|
|
30
32
|
}
|
|
31
|
-
export interface ConfigDashboardValueRow {
|
|
32
|
-
label: string;
|
|
33
|
-
value: string;
|
|
34
|
-
}
|
|
35
33
|
export interface ConfigRow {
|
|
36
34
|
definition: ConfigSettingDefinition;
|
|
37
35
|
value: ConfigValue;
|
|
@@ -45,7 +43,7 @@ export declare function projectContextUsage(data: ConfigContextData): {
|
|
|
45
43
|
freeTokens: number;
|
|
46
44
|
contextWindowTokens: number;
|
|
47
45
|
};
|
|
48
|
-
export declare function ConfigDashboard({ tab, snapshot, query, selectedIndex, searchFocused, status, usage,
|
|
46
|
+
export declare function ConfigDashboard({ tab, snapshot, query, selectedIndex, searchFocused, status, usage, effectiveValues, width, screenReader, maxRows, }: {
|
|
49
47
|
tab: ConfigDashboardTab;
|
|
50
48
|
snapshot: ConfigSettingsSnapshot;
|
|
51
49
|
query?: string;
|
|
@@ -53,8 +51,6 @@ export declare function ConfigDashboard({ tab, snapshot, query, selectedIndex, s
|
|
|
53
51
|
searchFocused?: boolean;
|
|
54
52
|
status?: ConfigStatusData;
|
|
55
53
|
usage?: ConfigUsageData;
|
|
56
|
-
settings?: readonly ConfigDashboardValueRow[];
|
|
57
|
-
stats?: readonly ConfigDashboardValueRow[];
|
|
58
54
|
effectiveValues?: ConfigEffectiveValues;
|
|
59
55
|
width: number;
|
|
60
56
|
screenReader: boolean;
|
|
@@ -2,11 +2,9 @@ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { CLAUDE_2_1_208_CONFIG_SETTINGS, configSettingValue, } from './config-settings.js';
|
|
4
4
|
const tabs = [
|
|
5
|
-
{ id: 'settings', label: 'Settings' },
|
|
6
5
|
{ id: 'status', label: 'Status' },
|
|
7
6
|
{ id: 'config', label: 'Config' },
|
|
8
7
|
{ id: 'usage', label: 'Usage' },
|
|
9
|
-
{ id: 'stats', label: 'Stats' },
|
|
10
8
|
];
|
|
11
9
|
function displayValue(definition, value) {
|
|
12
10
|
if (definition.id === 'permissionMode') {
|
|
@@ -104,7 +102,9 @@ function StatusRows({ status }) {
|
|
|
104
102
|
['cwd', status.cwd],
|
|
105
103
|
...(status.authSource ? [['Auth token', status.authSource]] : []),
|
|
106
104
|
...(status.baseUrl ? [['Provider base URL', status.baseUrl]] : []),
|
|
105
|
+
...(status.proxy ? [['Proxy', status.proxy]] : []),
|
|
107
106
|
['Model', status.model],
|
|
107
|
+
...(status.mcpSummary ? [['MCP servers', status.mcpSummary]] : []),
|
|
108
108
|
['Setting sources', status.settingSources.join(', ')],
|
|
109
109
|
];
|
|
110
110
|
return (_jsxs(_Fragment, { children: [rows.map(([label, value]) => (_jsxs(Box, { children: [_jsx(Box, { width: 22, children: _jsxs(Text, { children: [label, ":"] }) }), _jsx(Text, { children: value })] }, label))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Esc to cancel" })] }));
|
|
@@ -113,37 +113,23 @@ function UsageRows({ usage }) {
|
|
|
113
113
|
const seconds = (milliseconds) => `${Math.round(milliseconds / 1_000)}s`;
|
|
114
114
|
return (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: "Session" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Total cost: $", usage.costUsd.toFixed(4)] }), _jsxs(Text, { children: ["Total duration (API): ", seconds(usage.apiDurationMs)] }), _jsxs(Text, { children: ["Total duration (wall): ", seconds(usage.wallDurationMs)] }), _jsxs(Text, { children: ["Total code changes: ", usage.linesAdded, " lines added, ", usage.linesRemoved, ' ', "lines removed"] }), _jsxs(Text, { children: ["Usage: ", usage.usage.inputTokens, " input, ", usage.usage.outputTokens, ' ', "output, ", usage.usage.cacheReadInputTokens ?? 0, " cache read,", ' ', usage.usage.cacheCreationInputTokens ?? 0, " cache write"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Esc to cancel" })] }));
|
|
115
115
|
}
|
|
116
|
-
function
|
|
117
|
-
return rows.map((row) => (_jsxs(Box, { children: [_jsx(Box, { width: 28, children: _jsxs(Text, { children: [row.label, ":"] }) }), _jsx(Text, { children: row.value })] }, row.label)));
|
|
118
|
-
}
|
|
119
|
-
export function ConfigDashboard({ tab, snapshot, query = '', selectedIndex = 0, searchFocused = true, status, usage, settings, stats, effectiveValues, width, screenReader, maxRows = 18, }) {
|
|
116
|
+
export function ConfigDashboard({ tab, snapshot, query = '', selectedIndex = 0, searchFocused = true, status, usage, effectiveValues, width, screenReader, maxRows = 18, }) {
|
|
120
117
|
const rows = projectConfigRows(snapshot, query, effectiveValues);
|
|
121
118
|
let content;
|
|
122
119
|
switch (tab) {
|
|
123
|
-
case 'settings':
|
|
124
|
-
if (settings === undefined)
|
|
125
|
-
throw new Error('Settings tab requires measured settings data');
|
|
126
|
-
content = _jsx(ValueRows, { rows: settings });
|
|
127
|
-
break;
|
|
128
120
|
case 'status':
|
|
129
121
|
if (status === undefined)
|
|
130
122
|
throw new Error('Status tab requires measured status data');
|
|
131
123
|
content = _jsx(StatusRows, { status: status });
|
|
132
124
|
break;
|
|
133
125
|
case 'config':
|
|
134
|
-
content = (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: "Config" }), _jsx(
|
|
126
|
+
content = (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: "Config" }), _jsx(ConfigRows, { rows: rows, query: query, selectedIndex: Math.max(0, Math.min(selectedIndex, rows.length - 1)), searchFocused: searchFocused, maxRows: maxRows, screenReader: screenReader, width: width })] }));
|
|
135
127
|
break;
|
|
136
128
|
case 'usage':
|
|
137
129
|
if (usage === undefined)
|
|
138
130
|
throw new Error('Usage tab requires measured usage data');
|
|
139
131
|
content = _jsx(UsageRows, { usage: usage });
|
|
140
132
|
break;
|
|
141
|
-
case 'stats':
|
|
142
|
-
if (stats === undefined)
|
|
143
|
-
throw new Error('Stats tab requires measured stats data');
|
|
144
|
-
content =
|
|
145
|
-
stats.length === 0 ? (_jsx(Text, { children: "No stats available yet. Start using Praxis Code!" })) : (_jsx(ValueRows, { rows: stats }));
|
|
146
|
-
break;
|
|
147
133
|
}
|
|
148
134
|
return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? _jsx(Text, { children: '─'.repeat(Math.min(100, width)) }) : null, _jsx(DashboardTabs, { active: tab, screenReader: screenReader }), _jsx(Text, { children: " " }), content] }));
|
|
149
135
|
}
|
|
@@ -195,7 +195,7 @@ function withValueAtPath(source, path, value) {
|
|
|
195
195
|
return { ...source, [head]: withValueAtPath(child, tail, value) };
|
|
196
196
|
}
|
|
197
197
|
function validValue(definition, value) {
|
|
198
|
-
if (definition.values === 'language')
|
|
198
|
+
if (definition.values === 'language' || definition.id === 'model')
|
|
199
199
|
return (typeof value === 'string' &&
|
|
200
200
|
value.trim().length > 0 &&
|
|
201
201
|
value.length <= 256);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TranscriptItem, TuiDisplayMetadata } from './claude-style.js';
|
|
2
|
-
export declare function conversationExportText(
|
|
2
|
+
export declare function conversationExportText(_display: TuiDisplayMetadata, items: readonly TranscriptItem[]): string;
|
|
3
3
|
export declare function defaultConversationExportFilename(now?: Date): string;
|
|
4
4
|
export declare function conversationExportPath(cwd: string, filename: string): string;
|
|
5
5
|
export declare function writeConversationExport(path: string, text: string): Promise<void>;
|
|
@@ -4,17 +4,8 @@ function lines(text, prefix) {
|
|
|
4
4
|
const values = text.replace(/\n$/u, '').split('\n');
|
|
5
5
|
return values.map((line, index) => `${index === 0 ? prefix : ' '}${line}`);
|
|
6
6
|
}
|
|
7
|
-
export function conversationExportText(
|
|
8
|
-
const output = [
|
|
9
|
-
`╭─── Praxis Code v${display.version} ───╮`,
|
|
10
|
-
'│ Welcome back!',
|
|
11
|
-
`│ ${display.model ?? 'provider default'} · ${display.effort ?? 'high'} effort`,
|
|
12
|
-
`│ ${display.cwd}`,
|
|
13
|
-
'│ Tips for getting started: Run /help for commands',
|
|
14
|
-
'│ Shared with Claude Code: sessions, memory, skills',
|
|
15
|
-
'╰───',
|
|
16
|
-
'',
|
|
17
|
-
];
|
|
7
|
+
export function conversationExportText(_display, items) {
|
|
8
|
+
const output = [];
|
|
18
9
|
for (const item of items) {
|
|
19
10
|
if (item.kind === 'user')
|
|
20
11
|
output.push('', ...lines(item.text, '❯ '));
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export type TuiElicitationValue = string | number | boolean | string[];
|
|
2
|
+
interface PrimitiveSchema {
|
|
3
|
+
type?: unknown;
|
|
4
|
+
title?: unknown;
|
|
5
|
+
description?: unknown;
|
|
6
|
+
default?: unknown;
|
|
7
|
+
enum?: unknown;
|
|
8
|
+
enumNames?: unknown;
|
|
9
|
+
oneOf?: unknown;
|
|
10
|
+
anyOf?: unknown;
|
|
11
|
+
items?: unknown;
|
|
12
|
+
minLength?: unknown;
|
|
13
|
+
maxLength?: unknown;
|
|
14
|
+
pattern?: unknown;
|
|
15
|
+
format?: unknown;
|
|
16
|
+
minimum?: unknown;
|
|
17
|
+
maximum?: unknown;
|
|
18
|
+
exclusiveMinimum?: unknown;
|
|
19
|
+
exclusiveMaximum?: unknown;
|
|
20
|
+
minItems?: unknown;
|
|
21
|
+
maxItems?: unknown;
|
|
22
|
+
}
|
|
23
|
+
export interface TuiElicitationField {
|
|
24
|
+
name: string;
|
|
25
|
+
title: string;
|
|
26
|
+
description: string | undefined;
|
|
27
|
+
required: boolean;
|
|
28
|
+
schema: PrimitiveSchema;
|
|
29
|
+
kind: 'text' | 'number' | 'integer' | 'boolean' | 'enum' | 'multi-enum';
|
|
30
|
+
options: readonly {
|
|
31
|
+
value: string;
|
|
32
|
+
label: string;
|
|
33
|
+
}[];
|
|
34
|
+
}
|
|
35
|
+
export interface TuiElicitationFormState {
|
|
36
|
+
fields: readonly TuiElicitationField[];
|
|
37
|
+
values: Readonly<Record<string, TuiElicitationValue>>;
|
|
38
|
+
errors: Readonly<Record<string, string>>;
|
|
39
|
+
focusIndex: number;
|
|
40
|
+
expandedField: string | undefined;
|
|
41
|
+
optionIndex: number;
|
|
42
|
+
typeahead: string;
|
|
43
|
+
typeaheadAt: number;
|
|
44
|
+
}
|
|
45
|
+
export declare function createTuiElicitationForm(requestedSchema: Record<string, unknown> | undefined): TuiElicitationFormState;
|
|
46
|
+
export declare function focusedElicitationField(state: TuiElicitationFormState): TuiElicitationField | undefined;
|
|
47
|
+
export declare function elicitationTextValue(state: TuiElicitationFormState): string;
|
|
48
|
+
export declare function commitElicitationText(state: TuiElicitationFormState, raw: string): TuiElicitationFormState;
|
|
49
|
+
export declare function moveElicitationFocus(state: TuiElicitationFormState, direction: -1 | 1): TuiElicitationFormState;
|
|
50
|
+
export declare function unsetElicitationField(state: TuiElicitationFormState): TuiElicitationFormState;
|
|
51
|
+
export declare function toggleElicitationBoolean(state: TuiElicitationFormState): TuiElicitationFormState;
|
|
52
|
+
export declare function expandElicitationOptions(state: TuiElicitationFormState): TuiElicitationFormState;
|
|
53
|
+
export declare function moveElicitationOption(state: TuiElicitationFormState, direction: -1 | 1): TuiElicitationFormState;
|
|
54
|
+
export declare function selectElicitationOption(state: TuiElicitationFormState, collapse: boolean, ensureSelected?: boolean): TuiElicitationFormState;
|
|
55
|
+
export declare function typeaheadElicitationOption(state: TuiElicitationFormState, input: string, now?: number): TuiElicitationFormState;
|
|
56
|
+
export declare function validateTuiElicitationForm(state: TuiElicitationFormState): TuiElicitationFormState;
|
|
57
|
+
export declare function elicitationFormIsValid(state: TuiElicitationFormState): boolean;
|
|
58
|
+
export declare function McpElicitationUrl({ serverName, message, url, waiting, actionLabel, selection, screenReader, }: {
|
|
59
|
+
serverName: string;
|
|
60
|
+
message: string;
|
|
61
|
+
url: string;
|
|
62
|
+
waiting: boolean;
|
|
63
|
+
actionLabel: string;
|
|
64
|
+
selection: number;
|
|
65
|
+
screenReader: boolean;
|
|
66
|
+
}): import("react").JSX.Element;
|
|
67
|
+
export declare function McpElicitationForm({ serverName, message, state, input, screenReader, }: {
|
|
68
|
+
serverName: string;
|
|
69
|
+
message: string;
|
|
70
|
+
state: TuiElicitationFormState;
|
|
71
|
+
input: string;
|
|
72
|
+
screenReader: boolean;
|
|
73
|
+
}): import("react").JSX.Element;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=mcp-elicitation.d.ts.map
|