praxis-agent 0.16.0 → 0.17.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 +11 -5
- package/dist/application/background-agent-manager.d.ts +7 -1
- package/dist/application/background-agent-manager.js +148 -5
- package/dist/application/metered-model-completion.d.ts +15 -0
- package/dist/application/metered-model-completion.js +76 -0
- package/dist/application/model-compactor.js +25 -1
- package/dist/application/session-cost-tracker.d.ts +50 -0
- package/dist/application/session-cost-tracker.js +254 -0
- package/dist/application/session-service.d.ts +13 -0
- package/dist/application/session-service.js +594 -69
- package/dist/application/subagent-service.d.ts +11 -1
- package/dist/application/subagent-service.js +41 -1
- package/dist/application/tool-use-summary.d.ts +7 -2
- package/dist/application/tool-use-summary.js +42 -10
- package/dist/application/workflow-manager.d.ts +9 -4
- package/dist/application/workflow-manager.js +151 -7
- package/dist/cli/interactive.d.ts +5 -1
- package/dist/cli/interactive.js +227 -15
- package/dist/cli/protocol.d.ts +11 -0
- package/dist/cli/protocol.js +16 -1
- package/dist/cli/tui/claude-command-inventory.d.ts +27 -27
- package/dist/cli/tui/claude-command-inventory.js +26 -26
- package/dist/cli/tui/config-dashboard.d.ts +2 -9
- package/dist/cli/tui/config-dashboard.js +6 -6
- package/dist/cli/tui/cost-summary.d.ts +34 -0
- package/dist/cli/tui/cost-summary.js +192 -0
- package/dist/cli/tui/doctor-dashboard.d.ts +15 -0
- package/dist/cli/tui/doctor-dashboard.js +81 -0
- package/dist/cli/tui/slash-commands.js +10 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +192 -15
- package/dist/core/compaction.d.ts +2 -0
- package/dist/core/runtime.d.ts +38 -1
- package/dist/core/runtime.js +265 -11
- package/dist/persistence/claude-cost-state-store.d.ts +48 -0
- package/dist/persistence/claude-cost-state-store.js +335 -0
- package/dist/providers/anthropic-compatible.js +34 -9
- package/dist/providers/fallback-provider.d.ts +2 -1
- package/dist/providers/fallback-provider.js +28 -3
- package/dist/tools/line-changes.d.ts +8 -0
- package/dist/tools/line-changes.js +35 -0
- package/dist/tools/local-tools.js +30 -1
- package/package.json +2 -1
package/dist/cli/interactive.js
CHANGED
|
@@ -4,6 +4,7 @@ import { existsSync } from 'node:fs';
|
|
|
4
4
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { homedir, tmpdir } from 'node:os';
|
|
6
6
|
import { join, resolve } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
7
8
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
8
9
|
import { Box, Text, render, useApp, useInput } from 'ink';
|
|
9
10
|
import { permissionRuleValueToString } from '../permissions/permission-updates.js';
|
|
@@ -19,6 +20,8 @@ import { addTuiPermissionRule, loadTuiPermissionRules, removeTuiPermissionRule,
|
|
|
19
20
|
import { createRecentlyDeniedStore, } from './tui/recently-denied.js';
|
|
20
21
|
import { agentColorMessage, parseAgentColorInput, } from '../compatibility/claude/agent-color.js';
|
|
21
22
|
import { filterTuiSlashCommands, mergeTuiSlashCommands, slashCommandQuery, } from './tui/slash-commands.js';
|
|
23
|
+
import { runDoctor } from '../maintenance/doctor.js';
|
|
24
|
+
import { canonicalClaudeCostModelName, } from './tui/cost-summary.js';
|
|
22
25
|
import { createComposerEditor, deleteComposerBackward, deleteComposerForward, deleteComposerToEnd, deleteComposerToStart, deleteComposerWordBackward, insertComposerText, moveComposerCursor, moveComposerCursorByWord, } from './tui/composer-editor.js';
|
|
23
26
|
import { applyMentionReference, fileReferenceAtCursor, filterTuiMentionEntries, loadTuiFileEntries, } from './tui/file-picker.js';
|
|
24
27
|
import { editTuiPrompt, openTuiEditorFile, } from './tui/external-editor.js';
|
|
@@ -36,6 +39,7 @@ import { commitElicitationText, createTuiElicitationForm, elicitationFormIsValid
|
|
|
36
39
|
import { openTuiUrl } from './tui/open-url.js';
|
|
37
40
|
import { projectTuiToolPermission, ToolPermissionDialog, } from './tui/tool-permission.js';
|
|
38
41
|
import { ConfigDashboard, projectConfigRows } from './tui/config-dashboard.js';
|
|
42
|
+
import { DoctorDashboard } from './tui/doctor-dashboard.js';
|
|
39
43
|
import { SandboxDashboard, tuiSandboxTabs } from './tui/sandbox-dashboard.js';
|
|
40
44
|
import { createTuiSandboxStore, } from './tui/sandbox-settings.js';
|
|
41
45
|
import { loadConfigSettings, saveConfigSetting, configSettingDefinition, resolveConfigSettingsLocation, } from './tui/config-settings.js';
|
|
@@ -93,6 +97,15 @@ const PERMISSION_OPTIONS = [
|
|
|
93
97
|
description: 'Restrict changes to the plan file until approval.',
|
|
94
98
|
},
|
|
95
99
|
];
|
|
100
|
+
const ZERO_COST_SUMMARY = {
|
|
101
|
+
totalCostUsd: 0,
|
|
102
|
+
apiDurationMs: 0,
|
|
103
|
+
wallDurationMs: 0,
|
|
104
|
+
linesAdded: 0,
|
|
105
|
+
linesRemoved: 0,
|
|
106
|
+
hasUnknownModelCost: false,
|
|
107
|
+
modelUsage: [],
|
|
108
|
+
};
|
|
96
109
|
function selectionPrefix(selected, screenReader) {
|
|
97
110
|
if (selected)
|
|
98
111
|
return screenReader ? 'Selected: ' : '❯ ';
|
|
@@ -248,7 +261,7 @@ const HIDDEN_TUI_SLASH_COMMANDS = new Set([
|
|
|
248
261
|
'update',
|
|
249
262
|
'usage',
|
|
250
263
|
]);
|
|
251
|
-
export function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory = [], initialSessionColor, signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader = false, allowNewSession = true, resume, display = { version: 'dev', cwd: process.cwd() }, terminalWidth, slashCommands = EMPTY_SLASH_COMMANDS, agents = EMPTY_AGENTS, allowDangerouslySkipPermissions = false, additionalDirectories = [], diffLoader, fileLoader, externalEditor = editTuiPrompt, keybindingsConfigRoot, keybindingsFile = ensureTuiKeybindingsFile, keybindingsLoader = loadTuiKeybindings, keybindingsEditor = openTuiEditorFile, memoryFilesLoader = (configRoot, cwd) => loadTuiMemoryFiles({ configRoot, cwd }), memoryEditor = openTuiEditorFile, memoryFolderOpener = openTuiMemoryFolder, suspendProcess = suspendTuiProcess, clipboardReader = readTuiClipboard, clipboardWriter = writeTuiClipboard, sideQuestionClipboardWriter = writeTuiOsc52Clipboard, exportWriter = writeConversationExport, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver = resolveTuiWorkspaceDirectory, workspaceDirectoryCompleter = completeTuiWorkspaceDirectory, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener = openTuiUrl, releaseNotesLoader = (configRoot) => loadClaudeReleaseNotes({ configRoot }), settingSources, }) {
|
|
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, }) {
|
|
252
265
|
const { exit, suspendTerminal, waitUntilRenderFlush } = useApp();
|
|
253
266
|
const width = useTerminalWidth(terminalWidth);
|
|
254
267
|
const keybindingsRoot = useMemo(() => resolve(keybindingsConfigRoot ??
|
|
@@ -260,6 +273,25 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
260
273
|
const runtimeCwdRef = useRef(display.cwd);
|
|
261
274
|
const runtimeGitignoreRef = useRef(true);
|
|
262
275
|
const loadDiffSnapshot = useMemo(() => diffLoader ?? (() => loadGitDiff(runtimeCwd)), [diffLoader, runtimeCwd]);
|
|
276
|
+
const loadDoctorReport = useMemo(() => doctorLoader ??
|
|
277
|
+
(() => {
|
|
278
|
+
const configuredRoot = process.env.CLAUDE_CONFIG_DIR || undefined;
|
|
279
|
+
const configRoot = resolve(configuredRoot ?? resolve(homedir(), '.claude'));
|
|
280
|
+
const claudeStatePath = configuredRoot
|
|
281
|
+
? join(configRoot, '.claude.json')
|
|
282
|
+
: resolve(homedir(), '.claude.json');
|
|
283
|
+
return runDoctor({
|
|
284
|
+
version: display.version,
|
|
285
|
+
executablePath: resolve(process.argv[1] ??
|
|
286
|
+
fileURLToPath(new URL('../cli.js', import.meta.url))),
|
|
287
|
+
nodeExecutablePath: process.execPath,
|
|
288
|
+
nodeVersion: process.version,
|
|
289
|
+
configRoot,
|
|
290
|
+
claudeStatePath,
|
|
291
|
+
cwd: runtimeCwd,
|
|
292
|
+
environment: process.env,
|
|
293
|
+
});
|
|
294
|
+
}), [doctorLoader, runtimeCwd, display.version]);
|
|
263
295
|
const loadFiles = useMemo(() => fileLoader ??
|
|
264
296
|
(() => loadTuiFileEntries(runtimeCwd, {
|
|
265
297
|
respectGitignore: runtimeGitignoreRef.current,
|
|
@@ -355,6 +387,11 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
355
387
|
const btwCopiedTimerRef = useRef(null);
|
|
356
388
|
const [menu, setMenu] = useState(null);
|
|
357
389
|
const menuRef = useRef(null);
|
|
390
|
+
const configOpenGenerationRef = useRef(0);
|
|
391
|
+
const configUsageRequestRef = useRef(0);
|
|
392
|
+
const configOperationRef = useRef(null);
|
|
393
|
+
const doctorMenuGenerationRef = useRef(0);
|
|
394
|
+
const doctorOperationRef = useRef(null);
|
|
358
395
|
const [busy, setBusy] = useState(false);
|
|
359
396
|
const taskEntriesRef = useRef([]);
|
|
360
397
|
const mcpControllerRef = useRef(null);
|
|
@@ -924,6 +961,25 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
924
961
|
})();
|
|
925
962
|
}, [processSuspendRequested]);
|
|
926
963
|
const updateMenu = (next) => {
|
|
964
|
+
if (next === null && menuRef.current?.kind === 'config') {
|
|
965
|
+
configOpenGenerationRef.current += 1;
|
|
966
|
+
configUsageRequestRef.current += 1;
|
|
967
|
+
const closingConfigOperation = configOperationRef.current;
|
|
968
|
+
configOperationRef.current = null;
|
|
969
|
+
if (closingConfigOperation) {
|
|
970
|
+
setBusy(false);
|
|
971
|
+
onTurnChange?.(null);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
if (next === null && menuRef.current?.kind === 'doctor') {
|
|
975
|
+
doctorMenuGenerationRef.current += 1;
|
|
976
|
+
const closingDoctorOperation = doctorOperationRef.current;
|
|
977
|
+
doctorOperationRef.current = null;
|
|
978
|
+
if (closingDoctorOperation) {
|
|
979
|
+
setBusy(false);
|
|
980
|
+
onTurnChange?.(null);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
927
983
|
menuRef.current = next;
|
|
928
984
|
setMenu(next);
|
|
929
985
|
};
|
|
@@ -1634,32 +1690,181 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1634
1690
|
messageAge: position - 1,
|
|
1635
1691
|
});
|
|
1636
1692
|
};
|
|
1693
|
+
const loadCostUsage = async (sessionId) => {
|
|
1694
|
+
if (!sessionId)
|
|
1695
|
+
return ZERO_COST_SUMMARY;
|
|
1696
|
+
return withLocalCommands(async (commands) => {
|
|
1697
|
+
if (!commands.costSnapshot) {
|
|
1698
|
+
throw new Error('This interactive service cannot report session cost.');
|
|
1699
|
+
}
|
|
1700
|
+
const snapshot = await commands.costSnapshot(sessionId);
|
|
1701
|
+
return {
|
|
1702
|
+
totalCostUsd: snapshot.totalCostUsd,
|
|
1703
|
+
apiDurationMs: snapshot.apiDurationMs,
|
|
1704
|
+
wallDurationMs: snapshot.wallDurationMs,
|
|
1705
|
+
linesAdded: snapshot.linesAdded,
|
|
1706
|
+
linesRemoved: snapshot.linesRemoved,
|
|
1707
|
+
hasUnknownModelCost: snapshot.hasUnknownModelCost,
|
|
1708
|
+
modelUsage: Object.entries(snapshot.modelUsage).map(([model, usage]) => ({
|
|
1709
|
+
model,
|
|
1710
|
+
canonicalName: canonicalClaudeCostModelName(model),
|
|
1711
|
+
inputTokens: usage.inputTokens,
|
|
1712
|
+
outputTokens: usage.outputTokens,
|
|
1713
|
+
cacheReadInputTokens: usage.cacheReadInputTokens,
|
|
1714
|
+
cacheCreationInputTokens: usage.cacheCreationInputTokens,
|
|
1715
|
+
webSearchRequests: usage.webSearchRequests,
|
|
1716
|
+
costUsd: usage.costUsd,
|
|
1717
|
+
})),
|
|
1718
|
+
};
|
|
1719
|
+
});
|
|
1720
|
+
};
|
|
1721
|
+
const loadConfigMenuUsage = (generation, sessionId) => {
|
|
1722
|
+
const requestId = ++configUsageRequestRef.current;
|
|
1723
|
+
const loading = (async () => {
|
|
1724
|
+
setBusy(true);
|
|
1725
|
+
try {
|
|
1726
|
+
const usage = await loadCostUsage(sessionId);
|
|
1727
|
+
const current = menuRef.current;
|
|
1728
|
+
if (current?.kind === 'config' &&
|
|
1729
|
+
current.generation === generation &&
|
|
1730
|
+
current.tab === 'usage' &&
|
|
1731
|
+
sessionIdRef.current === sessionId &&
|
|
1732
|
+
configUsageRequestRef.current === requestId) {
|
|
1733
|
+
updateMenu({ ...current, usage });
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
catch (error) {
|
|
1737
|
+
const current = menuRef.current;
|
|
1738
|
+
if (current?.kind === 'config' &&
|
|
1739
|
+
current.generation === generation &&
|
|
1740
|
+
current.tab === 'usage' &&
|
|
1741
|
+
sessionIdRef.current === sessionId &&
|
|
1742
|
+
configUsageRequestRef.current === requestId) {
|
|
1743
|
+
warn(error);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
})();
|
|
1747
|
+
configOperationRef.current = loading;
|
|
1748
|
+
onTurnChange?.(loading);
|
|
1749
|
+
void loading.finally(() => {
|
|
1750
|
+
if (configOperationRef.current === loading) {
|
|
1751
|
+
configOperationRef.current = null;
|
|
1752
|
+
setBusy(false);
|
|
1753
|
+
onTurnChange?.(null);
|
|
1754
|
+
}
|
|
1755
|
+
});
|
|
1756
|
+
};
|
|
1637
1757
|
const openSettings = (tab) => {
|
|
1758
|
+
const generation = ++configOpenGenerationRef.current;
|
|
1759
|
+
const sessionId = sessionIdRef.current;
|
|
1760
|
+
const requestId = tab === 'usage' ? ++configUsageRequestRef.current : 0;
|
|
1638
1761
|
const initial = {
|
|
1639
1762
|
kind: 'config',
|
|
1763
|
+
generation,
|
|
1640
1764
|
snapshot: { settings: {}, state: {} },
|
|
1641
1765
|
tab,
|
|
1642
1766
|
selectedIndex: 0,
|
|
1643
1767
|
query: '',
|
|
1644
1768
|
searchFocused: tab === 'config',
|
|
1769
|
+
usage: ZERO_COST_SUMMARY,
|
|
1645
1770
|
};
|
|
1646
1771
|
updateMenu(initial);
|
|
1647
1772
|
const loading = (async () => {
|
|
1648
1773
|
setBusy(true);
|
|
1649
1774
|
try {
|
|
1650
|
-
const snapshot = await
|
|
1651
|
-
|
|
1652
|
-
|
|
1775
|
+
const [snapshot, usage] = await Promise.all([
|
|
1776
|
+
loadConfigSettings(configTarget),
|
|
1777
|
+
tab === 'usage'
|
|
1778
|
+
? loadCostUsage(sessionId)
|
|
1779
|
+
: Promise.resolve(ZERO_COST_SUMMARY),
|
|
1780
|
+
]);
|
|
1781
|
+
const current = menuRef.current;
|
|
1782
|
+
if (current?.kind === 'config' && current.generation === generation) {
|
|
1783
|
+
const next = { ...current, snapshot };
|
|
1784
|
+
if (current.tab === 'usage' &&
|
|
1785
|
+
sessionIdRef.current === sessionId &&
|
|
1786
|
+
configUsageRequestRef.current === requestId) {
|
|
1787
|
+
next.usage = usage;
|
|
1788
|
+
}
|
|
1789
|
+
updateMenu(next);
|
|
1790
|
+
}
|
|
1653
1791
|
}
|
|
1654
1792
|
catch (error) {
|
|
1655
|
-
|
|
1793
|
+
const current = menuRef.current;
|
|
1794
|
+
if (tab === 'usage') {
|
|
1795
|
+
if (current?.kind === 'config' &&
|
|
1796
|
+
current.generation === generation &&
|
|
1797
|
+
current.tab === 'usage' &&
|
|
1798
|
+
sessionIdRef.current === sessionId &&
|
|
1799
|
+
configUsageRequestRef.current === requestId) {
|
|
1800
|
+
warn(error);
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
else if (configOpenGenerationRef.current === generation) {
|
|
1804
|
+
warn(error);
|
|
1805
|
+
}
|
|
1656
1806
|
}
|
|
1657
|
-
|
|
1807
|
+
})();
|
|
1808
|
+
configOperationRef.current = loading;
|
|
1809
|
+
onTurnChange?.(loading);
|
|
1810
|
+
void loading.finally(() => {
|
|
1811
|
+
if (configOperationRef.current === loading) {
|
|
1812
|
+
configOperationRef.current = null;
|
|
1658
1813
|
setBusy(false);
|
|
1814
|
+
onTurnChange?.(null);
|
|
1815
|
+
}
|
|
1816
|
+
});
|
|
1817
|
+
};
|
|
1818
|
+
const openDoctor = () => {
|
|
1819
|
+
const generation = ++doctorMenuGenerationRef.current;
|
|
1820
|
+
updateMenu({
|
|
1821
|
+
kind: 'doctor',
|
|
1822
|
+
generation,
|
|
1823
|
+
loading: true,
|
|
1824
|
+
report: null,
|
|
1825
|
+
error: null,
|
|
1826
|
+
});
|
|
1827
|
+
const loading = (async () => {
|
|
1828
|
+
setBusy(true);
|
|
1829
|
+
try {
|
|
1830
|
+
const report = await loadDoctorReport();
|
|
1831
|
+
const current = menuRef.current;
|
|
1832
|
+
if (current?.kind === 'doctor' && current.generation === generation) {
|
|
1833
|
+
updateMenu({
|
|
1834
|
+
kind: 'doctor',
|
|
1835
|
+
generation,
|
|
1836
|
+
loading: false,
|
|
1837
|
+
report,
|
|
1838
|
+
error: null,
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
catch (error) {
|
|
1843
|
+
const current = menuRef.current;
|
|
1844
|
+
if (current?.kind === 'doctor' && current.generation === generation) {
|
|
1845
|
+
updateMenu({
|
|
1846
|
+
kind: 'doctor',
|
|
1847
|
+
generation,
|
|
1848
|
+
loading: false,
|
|
1849
|
+
report: null,
|
|
1850
|
+
error: redactSensitiveText(error instanceof Error ? error.message : String(error), sensitiveValues),
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
finally {
|
|
1855
|
+
if (doctorMenuGenerationRef.current === generation)
|
|
1856
|
+
setBusy(false);
|
|
1659
1857
|
}
|
|
1660
1858
|
})();
|
|
1859
|
+
doctorOperationRef.current = loading;
|
|
1661
1860
|
onTurnChange?.(loading);
|
|
1662
|
-
void loading.finally(() =>
|
|
1861
|
+
void loading.finally(() => {
|
|
1862
|
+
if (doctorOperationRef.current === loading) {
|
|
1863
|
+
doctorOperationRef.current = null;
|
|
1864
|
+
setBusy(false);
|
|
1865
|
+
onTurnChange?.(null);
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1663
1868
|
};
|
|
1664
1869
|
const exportConversation = (method) => {
|
|
1665
1870
|
if (method === 'file') {
|
|
@@ -3316,6 +3521,8 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3316
3521
|
selectedIndex: 0,
|
|
3317
3522
|
searchFocused: tab === 'config',
|
|
3318
3523
|
});
|
|
3524
|
+
if (tab === 'usage')
|
|
3525
|
+
loadConfigMenuUsage(earlyMenu.generation, sessionIdRef.current);
|
|
3319
3526
|
return;
|
|
3320
3527
|
}
|
|
3321
3528
|
if (earlyMenu.tab !== 'config')
|
|
@@ -4647,6 +4854,12 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
4647
4854
|
}
|
|
4648
4855
|
return;
|
|
4649
4856
|
}
|
|
4857
|
+
if (activeMenu.kind === 'doctor') {
|
|
4858
|
+
if (key.escape || value === '\u001B' || key.return) {
|
|
4859
|
+
updateMenu(null);
|
|
4860
|
+
}
|
|
4861
|
+
return;
|
|
4862
|
+
}
|
|
4650
4863
|
if (key.escape) {
|
|
4651
4864
|
updateMenu(null);
|
|
4652
4865
|
return;
|
|
@@ -5239,6 +5452,12 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
5239
5452
|
onTurnChange?.(loading);
|
|
5240
5453
|
void loading.finally(() => onTurnChange?.(null));
|
|
5241
5454
|
}
|
|
5455
|
+
else if (prompt === '/cost') {
|
|
5456
|
+
openSettings('usage');
|
|
5457
|
+
}
|
|
5458
|
+
else if (prompt === '/doctor') {
|
|
5459
|
+
openDoctor();
|
|
5460
|
+
}
|
|
5242
5461
|
else if (prompt === '/status') {
|
|
5243
5462
|
openSettings('status');
|
|
5244
5463
|
}
|
|
@@ -5446,14 +5665,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
5446
5665
|
...(statusProxy ? { proxy: statusProxy } : {}),
|
|
5447
5666
|
model: runtimeDisplay.model ?? 'default',
|
|
5448
5667
|
settingSources: statusSettingSources.split(', '),
|
|
5449
|
-
}, usage: {
|
|
5450
|
-
costUsd: costUsd ?? 0,
|
|
5451
|
-
apiDurationMs: 0,
|
|
5452
|
-
wallDurationMs: 0,
|
|
5453
|
-
linesAdded: 0,
|
|
5454
|
-
linesRemoved: 0,
|
|
5455
|
-
usage: usage ?? { inputTokens: 0, outputTokens: 0 },
|
|
5456
|
-
}, width: width, screenReader: axScreenReader })) : menu.kind === 'mcp' ? (_jsx(McpPanel, { model: menu.model, state: menu.state, width: width, screenReader: axScreenReader })) : menu.kind === 'tasks' ? (_jsx(TaskPanel, { tasks: menu.tasks, state: menu.state, width: width, screenReader: axScreenReader })) : menu.kind === 'memory' ? (_jsx(MemoryDashboard, { autoMemoryEnabled: menu.autoMemoryEnabled, entries: menu.entries, selectedIndex: menu.selectedIndex, openedIndex: menu.openedIndex, loading: menu.loading, width: width, screenReader: axScreenReader })) : menu.kind === 'hooks' ? (_jsx(HookDashboard, { configuration: menu.configuration, depth: menu.depth, eventIndex: menu.eventIndex, matcherIndex: menu.matcherIndex, hookIndex: menu.hookIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'list' ? (_jsx(ListDashboard, { title: menu.title, rows: menu.rows, emptyText: menu.emptyText, selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'model' ? (_jsx(ModelMenu, { options: modelOptions, effort: runtimePreferences.effort, selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'effort' ? (_jsx(SelectionMenu, { title: "Select effort", description: "Controls how much reasoning effort the provider should use.", options: EFFORT_OPTIONS.map((option) => ({
|
|
5668
|
+
}, usage: menu.usage, width: width, screenReader: axScreenReader })) : menu.kind === 'doctor' ? (_jsx(DoctorDashboard, { loading: menu.loading, report: menu.report, error: menu.error, width: width, screenReader: axScreenReader })) : menu.kind === 'mcp' ? (_jsx(McpPanel, { model: menu.model, state: menu.state, width: width, screenReader: axScreenReader })) : menu.kind === 'tasks' ? (_jsx(TaskPanel, { tasks: menu.tasks, state: menu.state, width: width, screenReader: axScreenReader })) : menu.kind === 'memory' ? (_jsx(MemoryDashboard, { autoMemoryEnabled: menu.autoMemoryEnabled, entries: menu.entries, selectedIndex: menu.selectedIndex, openedIndex: menu.openedIndex, loading: menu.loading, width: width, screenReader: axScreenReader })) : menu.kind === 'hooks' ? (_jsx(HookDashboard, { configuration: menu.configuration, depth: menu.depth, eventIndex: menu.eventIndex, matcherIndex: menu.matcherIndex, hookIndex: menu.hookIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'list' ? (_jsx(ListDashboard, { title: menu.title, rows: menu.rows, emptyText: menu.emptyText, selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'model' ? (_jsx(ModelMenu, { options: modelOptions, effort: runtimePreferences.effort, selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'effort' ? (_jsx(SelectionMenu, { title: "Select effort", description: "Controls how much reasoning effort the provider should use.", options: EFFORT_OPTIONS.map((option) => ({
|
|
5457
5669
|
label: option,
|
|
5458
5670
|
description: option === 'low'
|
|
5459
5671
|
? 'Fastest and least deliberative.'
|
package/dist/cli/protocol.d.ts
CHANGED
|
@@ -180,6 +180,7 @@ export interface CliRuntimeInfo {
|
|
|
180
180
|
cwd: string;
|
|
181
181
|
model: string;
|
|
182
182
|
contextWindowTokens?: number;
|
|
183
|
+
maxOutputTokens?: number;
|
|
183
184
|
tools: readonly string[];
|
|
184
185
|
mcpServers: readonly {
|
|
185
186
|
name: string;
|
|
@@ -203,6 +204,11 @@ export interface ProtocolResult {
|
|
|
203
204
|
durationApiMs?: number;
|
|
204
205
|
costUsd?: number;
|
|
205
206
|
modelUsage?: Readonly<Record<string, ModelUsage>>;
|
|
207
|
+
/**
|
|
208
|
+
* Exact per-model cost in USD. When present, each model's costUSD is taken
|
|
209
|
+
* from this map instead of assigning the total cost to runtimeInfo.model.
|
|
210
|
+
*/
|
|
211
|
+
modelCostUsd?: Readonly<Record<string, number>>;
|
|
206
212
|
}
|
|
207
213
|
export declare function createSuccessResult(result: ProtocolResult, info: CliRuntimeInfo, startedAt: number, modelTurns: number): Record<string, unknown>;
|
|
208
214
|
export declare function createErrorResult(message: string, sessionId: string, startedAt: number, modelTurns: number): Record<string, unknown>;
|
|
@@ -213,6 +219,11 @@ export declare function createErrorResult(message: string, sessionId: string, st
|
|
|
213
219
|
* the command.
|
|
214
220
|
*/
|
|
215
221
|
export declare function matchHeadlessColorCommand(prompt: string): string | undefined;
|
|
222
|
+
/**
|
|
223
|
+
* Returns true only for a trimmed exact `/cost` command. `/costs`, `/cost
|
|
224
|
+
* extra`, and any non-slash text are model prompts.
|
|
225
|
+
*/
|
|
226
|
+
export declare function isHeadlessCostCommand(prompt: string): boolean;
|
|
216
227
|
export declare function parseCliInvocation(argv: readonly string[]): CliInvocation;
|
|
217
228
|
export declare function readStreamJsonMessages(input: AsyncIterable<string | Uint8Array>): AsyncGenerator<StreamJsonMessage>;
|
|
218
229
|
export declare function readStreamUserMessages(input: AsyncIterable<string | Uint8Array>): AsyncGenerator<StreamUserMessage>;
|
package/dist/cli/protocol.js
CHANGED
|
@@ -35,7 +35,15 @@ export function createSuccessResult(result, info, startedAt, modelTurns) {
|
|
|
35
35
|
outputTokens: modelUsage.outputTokens,
|
|
36
36
|
cacheReadInputTokens: modelUsage.cacheReadInputTokens ?? 0,
|
|
37
37
|
cacheCreationInputTokens: modelUsage.cacheCreationInputTokens ?? 0,
|
|
38
|
-
costUSD:
|
|
38
|
+
costUSD: result.modelCostUsd?.[model] ??
|
|
39
|
+
(model === info.model ? (result.costUsd ?? null) : null),
|
|
40
|
+
...(modelUsage.webSearchRequests === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: { webSearchRequests: modelUsage.webSearchRequests }),
|
|
43
|
+
contextWindow: modelUsage.contextWindow ??
|
|
44
|
+
(model === info.model ? (info.contextWindowTokens ?? 0) : 0),
|
|
45
|
+
maxOutputTokens: modelUsage.maxOutputTokens ??
|
|
46
|
+
(model === info.model ? (info.maxOutputTokens ?? 0) : 0),
|
|
39
47
|
},
|
|
40
48
|
])),
|
|
41
49
|
permission_denials: [],
|
|
@@ -92,6 +100,13 @@ export function matchHeadlessColorCommand(prompt) {
|
|
|
92
100
|
return undefined;
|
|
93
101
|
return match[1] ?? '';
|
|
94
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Returns true only for a trimmed exact `/cost` command. `/costs`, `/cost
|
|
105
|
+
* extra`, and any non-slash text are model prompts.
|
|
106
|
+
*/
|
|
107
|
+
export function isHeadlessCostCommand(prompt) {
|
|
108
|
+
return prompt.trim() === '/cost';
|
|
109
|
+
}
|
|
95
110
|
const INPUT_FORMATS = ['text', 'stream-json'];
|
|
96
111
|
const OUTPUT_FORMATS = ['text', 'json', 'stream-json'];
|
|
97
112
|
const PERMISSION_MODES = [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ClaudeCommandDisposition = 'included' | 'deferred' | 'excluded';
|
|
1
|
+
export type ClaudeCommandDisposition = 'included' | 'required' | 'deferred' | 'excluded';
|
|
2
2
|
export type ClaudeCommandVisibility = 'visible' | 'hidden' | 'conditional';
|
|
3
3
|
export interface ClaudeCommandInventoryEntry {
|
|
4
4
|
name: string;
|
|
@@ -14,7 +14,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
14
14
|
readonly name: "advisor";
|
|
15
15
|
readonly disposition: "deferred";
|
|
16
16
|
readonly visibility: "conditional";
|
|
17
|
-
readonly reason: "Conditional advice mode is
|
|
17
|
+
readonly reason: "Conditional advice mode is an optional, demand-driven feature that does not block developer-core closure.";
|
|
18
18
|
}, {
|
|
19
19
|
readonly name: "agents";
|
|
20
20
|
readonly disposition: "included";
|
|
@@ -29,9 +29,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
29
29
|
readonly visibility: "visible";
|
|
30
30
|
}, {
|
|
31
31
|
readonly name: "chrome";
|
|
32
|
-
readonly disposition: "
|
|
32
|
+
readonly disposition: "excluded";
|
|
33
33
|
readonly visibility: "conditional";
|
|
34
|
-
readonly reason: "
|
|
34
|
+
readonly reason: "Claude-AI subscription-gated Chrome Beta; a future provider-neutral browser feature is separate from this command.";
|
|
35
35
|
}, {
|
|
36
36
|
readonly name: "clear";
|
|
37
37
|
readonly disposition: "included";
|
|
@@ -63,16 +63,16 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
63
63
|
readonly visibility: "conditional";
|
|
64
64
|
}, {
|
|
65
65
|
readonly name: "cost";
|
|
66
|
-
readonly disposition: "
|
|
66
|
+
readonly disposition: "included";
|
|
67
67
|
readonly visibility: "conditional";
|
|
68
|
-
readonly reason: "The dedicated /cost contract is
|
|
68
|
+
readonly reason: "The dedicated /cost contract is included; /status is not a substitute.";
|
|
69
69
|
}, {
|
|
70
70
|
readonly name: "diff";
|
|
71
71
|
readonly disposition: "included";
|
|
72
72
|
readonly visibility: "visible";
|
|
73
73
|
}, {
|
|
74
74
|
readonly name: "doctor";
|
|
75
|
-
readonly disposition: "
|
|
75
|
+
readonly disposition: "required";
|
|
76
76
|
readonly visibility: "conditional";
|
|
77
77
|
readonly reason: "Interactive /doctor is required; the top-level command is not a substitute.";
|
|
78
78
|
}, {
|
|
@@ -87,7 +87,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
87
87
|
readonly name: "fast";
|
|
88
88
|
readonly disposition: "deferred";
|
|
89
89
|
readonly visibility: "conditional";
|
|
90
|
-
readonly reason: "The /fast state flow is
|
|
90
|
+
readonly reason: "The /fast state flow is an optional convenience; model and effort controls cover the core flow, so it is deferred without blocking developer-core closure.";
|
|
91
91
|
}, {
|
|
92
92
|
readonly name: "files";
|
|
93
93
|
readonly disposition: "excluded";
|
|
@@ -95,9 +95,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
95
95
|
readonly reason: "The source command is restricted to the internal Ant user type.";
|
|
96
96
|
}, {
|
|
97
97
|
readonly name: "heapdump";
|
|
98
|
-
readonly disposition: "
|
|
98
|
+
readonly disposition: "excluded";
|
|
99
99
|
readonly visibility: "hidden";
|
|
100
|
-
readonly reason: "
|
|
100
|
+
readonly reason: "Hidden V8 maintainer diagnostic that writes a heap dump to Desktop; not a developer-core command.";
|
|
101
101
|
}, {
|
|
102
102
|
readonly name: "help";
|
|
103
103
|
readonly disposition: "included";
|
|
@@ -188,7 +188,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
188
188
|
readonly name: "stats";
|
|
189
189
|
readonly disposition: "deferred";
|
|
190
190
|
readonly visibility: "visible";
|
|
191
|
-
readonly reason: "Historical usage statistics are
|
|
191
|
+
readonly reason: "Historical usage statistics are an optional, demand-driven feature that does not block developer-core closure.";
|
|
192
192
|
}, {
|
|
193
193
|
readonly name: "status";
|
|
194
194
|
readonly disposition: "included";
|
|
@@ -261,7 +261,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
261
261
|
readonly name: "insights";
|
|
262
262
|
readonly disposition: "deferred";
|
|
263
263
|
readonly visibility: "visible";
|
|
264
|
-
readonly reason: "Retrospective insights are
|
|
264
|
+
readonly reason: "Retrospective insights are an optional, demand-driven feature that does not block developer-core closure.";
|
|
265
265
|
}, {
|
|
266
266
|
readonly name: "vim";
|
|
267
267
|
readonly disposition: "included";
|
|
@@ -277,24 +277,24 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
277
277
|
readonly visibility: "conditional";
|
|
278
278
|
}, {
|
|
279
279
|
readonly name: "buddy";
|
|
280
|
-
readonly disposition: "
|
|
280
|
+
readonly disposition: "excluded";
|
|
281
281
|
readonly visibility: "conditional";
|
|
282
|
-
readonly reason: "
|
|
282
|
+
readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
|
|
283
283
|
}, {
|
|
284
284
|
readonly name: "proactive";
|
|
285
|
-
readonly disposition: "
|
|
285
|
+
readonly disposition: "excluded";
|
|
286
286
|
readonly visibility: "conditional";
|
|
287
|
-
readonly reason: "
|
|
287
|
+
readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
|
|
288
288
|
}, {
|
|
289
289
|
readonly name: "brief";
|
|
290
|
-
readonly disposition: "
|
|
290
|
+
readonly disposition: "excluded";
|
|
291
291
|
readonly visibility: "conditional";
|
|
292
|
-
readonly reason: "
|
|
292
|
+
readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
|
|
293
293
|
}, {
|
|
294
294
|
readonly name: "assistant";
|
|
295
|
-
readonly disposition: "
|
|
295
|
+
readonly disposition: "excluded";
|
|
296
296
|
readonly visibility: "conditional";
|
|
297
|
-
readonly reason: "
|
|
297
|
+
readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
|
|
298
298
|
}, {
|
|
299
299
|
readonly name: "remote-control";
|
|
300
300
|
readonly disposition: "excluded";
|
|
@@ -309,17 +309,17 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
309
309
|
readonly name: "voice";
|
|
310
310
|
readonly disposition: "deferred";
|
|
311
311
|
readonly visibility: "conditional";
|
|
312
|
-
readonly reason: "Conditional voice input is
|
|
312
|
+
readonly reason: "Conditional voice input is an optional, demand-driven feature that does not block developer-core closure.";
|
|
313
313
|
}, {
|
|
314
314
|
readonly name: "think-back";
|
|
315
|
-
readonly disposition: "
|
|
315
|
+
readonly disposition: "excluded";
|
|
316
316
|
readonly visibility: "conditional";
|
|
317
|
-
readonly reason: "The
|
|
317
|
+
readonly reason: "The 2025 year-in-review campaign surface; a marketing flow, not a stable developer-core command.";
|
|
318
318
|
}, {
|
|
319
319
|
readonly name: "thinkback-play";
|
|
320
|
-
readonly disposition: "
|
|
320
|
+
readonly disposition: "excluded";
|
|
321
321
|
readonly visibility: "hidden";
|
|
322
|
-
readonly reason: "
|
|
322
|
+
readonly reason: "Hidden animation for the 2025 year-in-review campaign; a marketing flow, not a stable developer-core command.";
|
|
323
323
|
}, {
|
|
324
324
|
readonly name: "permissions";
|
|
325
325
|
readonly disposition: "included";
|
|
@@ -375,9 +375,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
375
375
|
readonly visibility: "conditional";
|
|
376
376
|
}, {
|
|
377
377
|
readonly name: "torch";
|
|
378
|
-
readonly disposition: "
|
|
378
|
+
readonly disposition: "excluded";
|
|
379
379
|
readonly visibility: "conditional";
|
|
380
|
-
readonly reason: "
|
|
380
|
+
readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
|
|
381
381
|
}];
|
|
382
382
|
export declare const CLAUDE_2_1_208_COMMAND_BY_NAME: ReadonlyMap<string, ClaudeCommandInventoryEntry>;
|
|
383
383
|
//# sourceMappingURL=claude-command-inventory.d.ts.map
|