praxis-agent 0.12.0 → 0.13.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 +9 -2
- package/dist/application/subagent-service.d.ts +1 -1
- package/dist/application/subagent-service.js +33 -2
- package/dist/cli/interactive.d.ts +4 -1
- package/dist/cli/interactive.js +63 -3
- package/dist/cli/tui/claude-command-inventory.d.ts +35 -19
- package/dist/cli/tui/claude-command-inventory.js +98 -18
- package/dist/cli/tui/release-notes.d.ts +10 -0
- package/dist/cli/tui/release-notes.js +60 -0
- package/dist/cli/tui/slash-commands.js +5 -0
- package/dist/cli/tui/status-line.d.ts +88 -0
- package/dist/cli/tui/status-line.js +234 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +46 -0
- package/dist/extensions/claude-extensions.d.ts +1 -0
- package/dist/extensions/claude-extensions.js +55 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,11 @@ Anthropic/OpenAI-compatible model access. Praxis deliberately excludes
|
|
|
15
15
|
accounts, organizations, billing, managed enterprise policy, remote control,
|
|
16
16
|
IDE surfaces, and telemetry control planes.
|
|
17
17
|
|
|
18
|
+
Claude Code 2.1.208 is the compatibility, architecture, and design baseline.
|
|
19
|
+
Every single-user CLI capability remains required unless it falls inside an
|
|
20
|
+
explicit exclusion above. A similar Praxis surface is not a substitute for the
|
|
21
|
+
corresponding Claude command or runtime contract.
|
|
22
|
+
|
|
18
23
|
## Requirements
|
|
19
24
|
|
|
20
25
|
- macOS or Linux
|
|
@@ -87,8 +92,10 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
|
|
|
87
92
|
`/rewind`, runtime `/cd`, transcript-free
|
|
88
93
|
`/btw` side questions with background-Agent handoff, interactive
|
|
89
94
|
`/background` terminal handoff, unified `/status`/`/config`/`/usage` settings
|
|
90
|
-
tabs, `/sandbox` mode/dependency/override/config controls,
|
|
91
|
-
|
|
95
|
+
tabs, `/sandbox` mode/dependency/override/config controls, local cached
|
|
96
|
+
`/release-notes`, Claude-compatible `/statusline` command execution and setup
|
|
97
|
+
agent, `/mcp`, `/memory` shared instruction and auto-memory access, and live
|
|
98
|
+
extension-reload controls,
|
|
92
99
|
cursor/history composer, per-session model/effort/permission controls,
|
|
93
100
|
context/status/skill/task dashboards, prompt stash and continuation shortcuts,
|
|
94
101
|
filterable `@` file and agent references, composer undo, `Ctrl+G` external
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ClaudeSidechainPermissionMode } from '../compatibility/claude/sidechain.js';
|
|
2
2
|
import { type ContextAssembler } from '../core/context.js';
|
|
3
3
|
import { type ModelProvider, type ModelToolCall, type ModelToolDefinition, type ModelUsage, type PermissionResolver, type PermissionApproval, type PermissionDecision, type PermissionUpdate, type RuntimeEventSink, type ToolExecutionContext, type ToolExecutionResult, type ToolRegistry } from '../core/runtime.js';
|
|
4
|
-
import type
|
|
4
|
+
import { type ClaudeExtensionCatalog } from '../extensions/claude-extensions.js';
|
|
5
5
|
import type { ClaudeHookRunner } from '../hooks/claude-hooks.js';
|
|
6
6
|
import { type BackgroundAgentSnapshot } from './background-agent-manager.js';
|
|
7
7
|
export interface WorkflowAgentRunOptions {
|
|
@@ -11,6 +11,7 @@ import { createClaudeHookAttachmentEntries, translateProviderEvents, } from '../
|
|
|
11
11
|
import { injectFirstUserMessageContext, } from '../core/context.js';
|
|
12
12
|
import { ContextBudget } from '../core/context-budget.js';
|
|
13
13
|
import { AgentRuntime, } from '../core/runtime.js';
|
|
14
|
+
import { BUILTIN_STATUSLINE_AGENT_PATH, } from '../extensions/claude-extensions.js';
|
|
14
15
|
import { ClaudeHookToolCoordinator } from '../hooks/claude-hook-tools.js';
|
|
15
16
|
import { ClaudeSidechainStore } from '../persistence/claude-sidechain-store.js';
|
|
16
17
|
import { InMemorySidechainStore } from '../persistence/in-memory-sidechain-store.js';
|
|
@@ -79,6 +80,29 @@ export class StructuredOutputRegistry {
|
|
|
79
80
|
});
|
|
80
81
|
}
|
|
81
82
|
}
|
|
83
|
+
class RestrictedToolRegistry {
|
|
84
|
+
base;
|
|
85
|
+
allowed;
|
|
86
|
+
constructor(base, names) {
|
|
87
|
+
this.base = base;
|
|
88
|
+
this.allowed = new Set(names);
|
|
89
|
+
}
|
|
90
|
+
definitions() {
|
|
91
|
+
return this.base
|
|
92
|
+
.definitions()
|
|
93
|
+
.filter((definition) => this.allowed.has(definition.name));
|
|
94
|
+
}
|
|
95
|
+
prepare(call, context) {
|
|
96
|
+
if (!this.allowed.has(call.name))
|
|
97
|
+
throw new Error(`Tool ${call.name} is unavailable to this agent`);
|
|
98
|
+
return this.base.prepare(call, context);
|
|
99
|
+
}
|
|
100
|
+
execute(call, context) {
|
|
101
|
+
if (!this.allowed.has(call.name))
|
|
102
|
+
throw new Error(`Tool ${call.name} is unavailable to this agent`);
|
|
103
|
+
return this.base.execute(call, context);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
82
106
|
function parseAgentInput(call) {
|
|
83
107
|
const allowed = new Set([
|
|
84
108
|
'description',
|
|
@@ -942,9 +966,16 @@ export class ClaudeSubagentExecutor {
|
|
|
942
966
|
permission_mode: options.input.permissionMode ?? 'default',
|
|
943
967
|
};
|
|
944
968
|
const nestedTools = this.registry(String(options.root.sessionId), options.spawnDepth, () => options.promptId);
|
|
945
|
-
const
|
|
946
|
-
|
|
969
|
+
const builtInStatusLineAgent = this.options.extensions?.agent(options.input.subagentType)?.path ===
|
|
970
|
+
BUILTIN_STATUSLINE_AGENT_PATH;
|
|
971
|
+
const scopedTools = builtInStatusLineAgent
|
|
972
|
+
? new RestrictedToolRegistry(nestedTools, ['Read', 'Edit'])
|
|
947
973
|
: nestedTools;
|
|
974
|
+
const agentTools = options.outputSchema
|
|
975
|
+
? new StructuredOutputRegistry(builtInStatusLineAgent
|
|
976
|
+
? new RestrictedToolRegistry(structuredOnlyTools, ['Read', 'Edit'])
|
|
977
|
+
: structuredOnlyTools, options.outputSchema, options.structuredOutput)
|
|
978
|
+
: scopedTools;
|
|
948
979
|
const runtimeTools = this.options.hooks
|
|
949
980
|
? new ClaudeHookToolCoordinator({
|
|
950
981
|
tools: agentTools,
|
|
@@ -175,8 +175,10 @@ interface InteractiveAppProps {
|
|
|
175
175
|
runtimeSettingsTarget?: ConfigSettingsTarget;
|
|
176
176
|
notificationWriter?: TuiNotificationWriter;
|
|
177
177
|
elicitationUrlOpener?: (url: string) => void | Promise<void>;
|
|
178
|
+
releaseNotesLoader?: (configRoot: string) => Promise<string>;
|
|
179
|
+
settingSources?: readonly ClaudeResourceScope[];
|
|
178
180
|
}
|
|
179
|
-
export declare function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory, signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader, allowNewSession, resume, display, terminalWidth, slashCommands, agents, allowDangerouslySkipPermissions, additionalDirectories, diffLoader, fileLoader, externalEditor, keybindingsConfigRoot, keybindingsFile, keybindingsLoader, keybindingsEditor, memoryFilesLoader, memoryEditor, memoryFolderOpener, suspendProcess, clipboardReader, clipboardWriter, sideQuestionClipboardWriter, exportWriter, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver, workspaceDirectoryCompleter, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener, }: InteractiveAppProps): import("react").JSX.Element;
|
|
181
|
+
export declare function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory, signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader, allowNewSession, resume, display, terminalWidth, slashCommands, agents, allowDangerouslySkipPermissions, additionalDirectories, diffLoader, fileLoader, externalEditor, keybindingsConfigRoot, keybindingsFile, keybindingsLoader, keybindingsEditor, memoryFilesLoader, memoryEditor, memoryFolderOpener, suspendProcess, clipboardReader, clipboardWriter, sideQuestionClipboardWriter, exportWriter, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver, workspaceDirectoryCompleter, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener, releaseNotesLoader, settingSources, }: InteractiveAppProps): import("react").JSX.Element;
|
|
180
182
|
export declare function runInteractive(options: {
|
|
181
183
|
factory: InteractiveServiceFactory;
|
|
182
184
|
initialPrompt?: string;
|
|
@@ -192,6 +194,7 @@ export declare function runInteractive(options: {
|
|
|
192
194
|
onBackground?: (request: InteractiveBackgroundRequest) => Promise<InteractiveBackgroundResult>;
|
|
193
195
|
onBackgrounded?: (result: InteractiveBackgroundResult) => void;
|
|
194
196
|
runtimeSettings?: PraxisRuntimeSettings;
|
|
197
|
+
settingSources?: readonly ClaudeResourceScope[];
|
|
195
198
|
}): Promise<number>;
|
|
196
199
|
export {};
|
|
197
200
|
//# sourceMappingURL=interactive.d.ts.map
|
package/dist/cli/interactive.js
CHANGED
|
@@ -12,6 +12,8 @@ import { claudePermissionActionKey, claudePermissionRuleMatches, } from '../perm
|
|
|
12
12
|
import { redactSensitiveText, sensitiveEnvironmentValues, } from '../platform/sensitive-data.js';
|
|
13
13
|
import { CommandPalette, BtwPanel, Composer, DiffDashboard, DialogFrame, ExternalEditorWait, HelpMenu, HookDashboard, ListDashboard, MemoryDashboard, MentionPicker, ModelMenu, PermissionDashboard, SelectionMenu, SessionPicker, ThemePicker, CustomThemeEditor, Transcript, WelcomePanel, useTerminalWidth, } from './tui/claude-style.js';
|
|
14
14
|
import { loadTuiMemoryFiles, openTuiMemoryFolder, } from './tui/memory-files.js';
|
|
15
|
+
import { loadClaudeReleaseNotes } from './tui/release-notes.js';
|
|
16
|
+
import { createClaudeStatusLineInput, StatusLine } from './tui/status-line.js';
|
|
15
17
|
import { loadGitDiff, visiblePatchLines, } from './tui/git-diff.js';
|
|
16
18
|
import { addTuiPermissionRule, loadTuiPermissionRules, removeTuiPermissionRule, } from './tui/permission-settings.js';
|
|
17
19
|
import { createRecentlyDeniedStore, } from './tui/recently-denied.js';
|
|
@@ -245,7 +247,7 @@ const HIDDEN_TUI_SLASH_COMMANDS = new Set([
|
|
|
245
247
|
'update',
|
|
246
248
|
'usage',
|
|
247
249
|
]);
|
|
248
|
-
export function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory = [], signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader = false, allowNewSession = true, resume, display = { version: 'dev', cwd: process.cwd() }, terminalWidth, slashCommands = EMPTY_SLASH_COMMANDS, agents = EMPTY_AGENTS, allowDangerouslySkipPermissions = false, additionalDirectories = [], diffLoader, fileLoader, externalEditor = editTuiPrompt, keybindingsConfigRoot, keybindingsFile = ensureTuiKeybindingsFile, keybindingsLoader = loadTuiKeybindings, keybindingsEditor = openTuiEditorFile, memoryFilesLoader = (configRoot, cwd) => loadTuiMemoryFiles({ configRoot, cwd }), memoryEditor = openTuiEditorFile, memoryFolderOpener = openTuiMemoryFolder, suspendProcess = suspendTuiProcess, clipboardReader = readTuiClipboard, clipboardWriter = writeTuiClipboard, sideQuestionClipboardWriter = writeTuiOsc52Clipboard, exportWriter = writeConversationExport, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver = resolveTuiWorkspaceDirectory, workspaceDirectoryCompleter = completeTuiWorkspaceDirectory, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener = openTuiUrl, }) {
|
|
250
|
+
export function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory = [], signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader = false, allowNewSession = true, resume, display = { version: 'dev', cwd: process.cwd() }, terminalWidth, slashCommands = EMPTY_SLASH_COMMANDS, agents = EMPTY_AGENTS, allowDangerouslySkipPermissions = false, additionalDirectories = [], diffLoader, fileLoader, externalEditor = editTuiPrompt, keybindingsConfigRoot, keybindingsFile = ensureTuiKeybindingsFile, keybindingsLoader = loadTuiKeybindings, keybindingsEditor = openTuiEditorFile, memoryFilesLoader = (configRoot, cwd) => loadTuiMemoryFiles({ configRoot, cwd }), memoryEditor = openTuiEditorFile, memoryFolderOpener = openTuiMemoryFolder, suspendProcess = suspendTuiProcess, clipboardReader = readTuiClipboard, clipboardWriter = writeTuiClipboard, sideQuestionClipboardWriter = writeTuiOsc52Clipboard, exportWriter = writeConversationExport, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver = resolveTuiWorkspaceDirectory, workspaceDirectoryCompleter = completeTuiWorkspaceDirectory, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener = openTuiUrl, releaseNotesLoader = (configRoot) => loadClaudeReleaseNotes({ configRoot }), settingSources, }) {
|
|
249
251
|
const { exit, suspendTerminal, waitUntilRenderFlush } = useApp();
|
|
250
252
|
const width = useTerminalWidth(terminalWidth);
|
|
251
253
|
const keybindingsRoot = useMemo(() => resolve(keybindingsConfigRoot ??
|
|
@@ -299,6 +301,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
299
301
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
300
302
|
const selectedIndexRef = useRef(0);
|
|
301
303
|
const [sessionId, setSessionId] = useState(resume?.sessionId ?? null);
|
|
304
|
+
const statusLineSessionId = useRef(resume?.sessionId ?? randomUUID());
|
|
302
305
|
const sessionIdRef = useRef(resume?.sessionId ?? null);
|
|
303
306
|
sessionIdRef.current = sessionId;
|
|
304
307
|
const [sessionName, setSessionName] = useState(null);
|
|
@@ -4799,11 +4802,13 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
4799
4802
|
updateMenu({ kind: 'help', tabIndex: 0, selectedIndex: 0 });
|
|
4800
4803
|
}
|
|
4801
4804
|
else if (prompt === '/new') {
|
|
4805
|
+
statusLineSessionId.current = randomUUID();
|
|
4802
4806
|
setSessionId(null);
|
|
4803
4807
|
setPendingFork(false);
|
|
4804
4808
|
append({ kind: 'notice', text: 'Started a new session.' });
|
|
4805
4809
|
}
|
|
4806
4810
|
else if (prompt === '/clear') {
|
|
4811
|
+
statusLineSessionId.current = randomUUID();
|
|
4807
4812
|
setSessionId(null);
|
|
4808
4813
|
setPendingFork(false);
|
|
4809
4814
|
setHistory([]);
|
|
@@ -5176,6 +5181,27 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
5176
5181
|
else if (prompt === '/status') {
|
|
5177
5182
|
openSettings('status');
|
|
5178
5183
|
}
|
|
5184
|
+
else if (prompt === '/release-notes') {
|
|
5185
|
+
const loading = (async () => {
|
|
5186
|
+
setBusy(true);
|
|
5187
|
+
setStatus('loading release notes');
|
|
5188
|
+
try {
|
|
5189
|
+
append({
|
|
5190
|
+
kind: 'local-result',
|
|
5191
|
+
text: await releaseNotesLoader(keybindingsRoot),
|
|
5192
|
+
});
|
|
5193
|
+
}
|
|
5194
|
+
catch (error) {
|
|
5195
|
+
warn(error);
|
|
5196
|
+
}
|
|
5197
|
+
finally {
|
|
5198
|
+
setBusy(false);
|
|
5199
|
+
setStatus('ready');
|
|
5200
|
+
}
|
|
5201
|
+
})();
|
|
5202
|
+
onTurnChange?.(loading);
|
|
5203
|
+
void loading.finally(() => onTurnChange?.(null));
|
|
5204
|
+
}
|
|
5179
5205
|
else if (prompt === '/config') {
|
|
5180
5206
|
openSettings('config');
|
|
5181
5207
|
}
|
|
@@ -5395,7 +5421,39 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
5395
5421
|
? { prStatus: `PR #${activeSessionSummary.prNumber}` }
|
|
5396
5422
|
: {}), shortcutsVisible: shortcutsVisible, ...(editorFooterMessage === undefined
|
|
5397
5423
|
? {}
|
|
5398
|
-
: { footerMessage: editorFooterMessage }) })
|
|
5424
|
+
: { footerMessage: editorFooterMessage }) }), _jsx(StatusLine, { configRoot: keybindingsRoot, cwd: runtimeCwd, input: createClaudeStatusLineInput({
|
|
5425
|
+
configRoot: keybindingsRoot,
|
|
5426
|
+
cwd: runtimeCwd,
|
|
5427
|
+
projectDir: display.cwd,
|
|
5428
|
+
sessionId: sessionId ?? statusLineSessionId.current,
|
|
5429
|
+
sessionName,
|
|
5430
|
+
...(runtimeDisplay.model === undefined
|
|
5431
|
+
? {}
|
|
5432
|
+
: { model: runtimeDisplay.model }),
|
|
5433
|
+
version: runtimeDisplay.version,
|
|
5434
|
+
outputStyle: runtimeSettings.outputStyle,
|
|
5435
|
+
permissionMode: runtimePreferences.permissionMode,
|
|
5436
|
+
additionalDirectories: runtimePreferences.additionalDirectories,
|
|
5437
|
+
...(usage === undefined ? {} : { usage }),
|
|
5438
|
+
...(costUsd === undefined ? {} : { costUsd }),
|
|
5439
|
+
...(runtimeDisplay.contextWindowTokens === undefined
|
|
5440
|
+
? {}
|
|
5441
|
+
: {
|
|
5442
|
+
contextWindowTokens: runtimeDisplay.contextWindowTokens,
|
|
5443
|
+
}),
|
|
5444
|
+
...(runtimeSettings.editor === 'vim'
|
|
5445
|
+
? { vimMode: vimInsertMode ? 'INSERT' : 'NORMAL' }
|
|
5446
|
+
: {}),
|
|
5447
|
+
}), refreshKey: [
|
|
5448
|
+
history.length,
|
|
5449
|
+
runtimePreferences.permissionMode,
|
|
5450
|
+
runtimeDisplay.model,
|
|
5451
|
+
runtimeSettings.outputStyle,
|
|
5452
|
+
vimInsertMode,
|
|
5453
|
+
sessionName,
|
|
5454
|
+
usage?.inputTokens,
|
|
5455
|
+
usage?.outputTokens,
|
|
5456
|
+
].join(':'), ...(settingSources === undefined ? {} : { settingSources }) })] }))] })) }) }));
|
|
5399
5457
|
}
|
|
5400
5458
|
export async function runInteractive(options) {
|
|
5401
5459
|
const controller = new AbortController();
|
|
@@ -5484,7 +5542,9 @@ export async function runInteractive(options) {
|
|
|
5484
5542
|
let cleanup = null;
|
|
5485
5543
|
let backgrounded;
|
|
5486
5544
|
rendererChange = null;
|
|
5487
|
-
const instance = render(_jsx(InteractiveApp, { factory: options.factory, initialSessions: initialSessions, slashCommands: initialSlashCommands, agents: initialAgents, initialHistory: history, runtimeSettings: currentRuntimeSettings,
|
|
5545
|
+
const instance = render(_jsx(InteractiveApp, { factory: options.factory, initialSessions: initialSessions, slashCommands: initialSlashCommands, agents: initialAgents, initialHistory: history, runtimeSettings: currentRuntimeSettings, ...(options.settingSources === undefined
|
|
5546
|
+
? {}
|
|
5547
|
+
: { settingSources: options.settingSources }), initialThemeSettings: initialThemeSettings, ...(initialThemeLoadError === undefined
|
|
5488
5548
|
? {}
|
|
5489
5549
|
: { initialThemeLoadError }), ...(currentInitialPrompt === undefined
|
|
5490
5550
|
? {}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ClaudeCommandDisposition = 'included' | 'excluded';
|
|
1
|
+
export type ClaudeCommandDisposition = 'included' | 'deferred' | 'excluded';
|
|
2
2
|
export type ClaudeCommandVisibility = 'visible' | 'hidden' | 'conditional';
|
|
3
3
|
export interface ClaudeCommandInventoryEntry {
|
|
4
4
|
name: string;
|
|
@@ -12,8 +12,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
12
12
|
readonly visibility: "visible";
|
|
13
13
|
}, {
|
|
14
14
|
readonly name: "advisor";
|
|
15
|
-
readonly disposition: "
|
|
15
|
+
readonly disposition: "deferred";
|
|
16
16
|
readonly visibility: "conditional";
|
|
17
|
+
readonly reason: "Conditional advice mode is required parity work and is not implemented yet.";
|
|
17
18
|
}, {
|
|
18
19
|
readonly name: "agents";
|
|
19
20
|
readonly disposition: "included";
|
|
@@ -28,17 +29,18 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
28
29
|
readonly visibility: "visible";
|
|
29
30
|
}, {
|
|
30
31
|
readonly name: "chrome";
|
|
31
|
-
readonly disposition: "
|
|
32
|
+
readonly disposition: "deferred";
|
|
32
33
|
readonly visibility: "conditional";
|
|
33
|
-
readonly reason: "Chrome integration is
|
|
34
|
+
readonly reason: "The CLI-driven Chrome integration is required parity work and is not implemented yet.";
|
|
34
35
|
}, {
|
|
35
36
|
readonly name: "clear";
|
|
36
37
|
readonly disposition: "included";
|
|
37
38
|
readonly visibility: "visible";
|
|
38
39
|
}, {
|
|
39
40
|
readonly name: "color";
|
|
40
|
-
readonly disposition: "
|
|
41
|
+
readonly disposition: "deferred";
|
|
41
42
|
readonly visibility: "visible";
|
|
43
|
+
readonly reason: "The dedicated /color contract is required; /theme is not a substitute.";
|
|
42
44
|
}, {
|
|
43
45
|
readonly name: "compact";
|
|
44
46
|
readonly disposition: "included";
|
|
@@ -62,16 +64,18 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
62
64
|
readonly visibility: "conditional";
|
|
63
65
|
}, {
|
|
64
66
|
readonly name: "cost";
|
|
65
|
-
readonly disposition: "
|
|
67
|
+
readonly disposition: "deferred";
|
|
66
68
|
readonly visibility: "conditional";
|
|
69
|
+
readonly reason: "The dedicated /cost contract is required; /status is not a substitute.";
|
|
67
70
|
}, {
|
|
68
71
|
readonly name: "diff";
|
|
69
72
|
readonly disposition: "included";
|
|
70
73
|
readonly visibility: "visible";
|
|
71
74
|
}, {
|
|
72
75
|
readonly name: "doctor";
|
|
73
|
-
readonly disposition: "
|
|
76
|
+
readonly disposition: "deferred";
|
|
74
77
|
readonly visibility: "conditional";
|
|
78
|
+
readonly reason: "Interactive /doctor is required; the top-level command is not a substitute.";
|
|
75
79
|
}, {
|
|
76
80
|
readonly name: "effort";
|
|
77
81
|
readonly disposition: "included";
|
|
@@ -82,8 +86,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
82
86
|
readonly visibility: "visible";
|
|
83
87
|
}, {
|
|
84
88
|
readonly name: "fast";
|
|
85
|
-
readonly disposition: "
|
|
89
|
+
readonly disposition: "deferred";
|
|
86
90
|
readonly visibility: "conditional";
|
|
91
|
+
readonly reason: "The /fast state flow is required; model and effort controls are not a substitute.";
|
|
87
92
|
}, {
|
|
88
93
|
readonly name: "files";
|
|
89
94
|
readonly disposition: "excluded";
|
|
@@ -91,8 +96,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
91
96
|
readonly reason: "The source command is restricted to the internal Ant user type.";
|
|
92
97
|
}, {
|
|
93
98
|
readonly name: "heapdump";
|
|
94
|
-
readonly disposition: "
|
|
99
|
+
readonly disposition: "deferred";
|
|
95
100
|
readonly visibility: "hidden";
|
|
101
|
+
readonly reason: "The hidden heap-diagnostic contract is required parity work and is not implemented yet.";
|
|
96
102
|
}, {
|
|
97
103
|
readonly name: "help";
|
|
98
104
|
readonly disposition: "included";
|
|
@@ -181,8 +187,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
181
187
|
readonly visibility: "visible";
|
|
182
188
|
}, {
|
|
183
189
|
readonly name: "stats";
|
|
184
|
-
readonly disposition: "
|
|
190
|
+
readonly disposition: "deferred";
|
|
185
191
|
readonly visibility: "visible";
|
|
192
|
+
readonly reason: "Historical usage statistics are required parity work and are not implemented yet.";
|
|
186
193
|
}, {
|
|
187
194
|
readonly name: "status";
|
|
188
195
|
readonly disposition: "included";
|
|
@@ -253,8 +260,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
253
260
|
readonly reason: "The source command is the Claude subscription plan-usage panel.";
|
|
254
261
|
}, {
|
|
255
262
|
readonly name: "insights";
|
|
256
|
-
readonly disposition: "
|
|
263
|
+
readonly disposition: "deferred";
|
|
257
264
|
readonly visibility: "visible";
|
|
265
|
+
readonly reason: "Retrospective insights are required parity work and are not implemented yet.";
|
|
258
266
|
}, {
|
|
259
267
|
readonly name: "vim";
|
|
260
268
|
readonly disposition: "included";
|
|
@@ -270,20 +278,24 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
270
278
|
readonly visibility: "conditional";
|
|
271
279
|
}, {
|
|
272
280
|
readonly name: "buddy";
|
|
273
|
-
readonly disposition: "
|
|
281
|
+
readonly disposition: "deferred";
|
|
274
282
|
readonly visibility: "conditional";
|
|
283
|
+
readonly reason: "This conditional source mode is required parity work and is not implemented yet.";
|
|
275
284
|
}, {
|
|
276
285
|
readonly name: "proactive";
|
|
277
|
-
readonly disposition: "
|
|
286
|
+
readonly disposition: "deferred";
|
|
278
287
|
readonly visibility: "conditional";
|
|
288
|
+
readonly reason: "This conditional source mode is required parity work and is not implemented yet.";
|
|
279
289
|
}, {
|
|
280
290
|
readonly name: "brief";
|
|
281
|
-
readonly disposition: "
|
|
291
|
+
readonly disposition: "deferred";
|
|
282
292
|
readonly visibility: "conditional";
|
|
293
|
+
readonly reason: "This conditional source mode is required parity work and is not implemented yet.";
|
|
283
294
|
}, {
|
|
284
295
|
readonly name: "assistant";
|
|
285
|
-
readonly disposition: "
|
|
296
|
+
readonly disposition: "deferred";
|
|
286
297
|
readonly visibility: "conditional";
|
|
298
|
+
readonly reason: "This conditional source mode is required parity work and is not implemented yet.";
|
|
287
299
|
}, {
|
|
288
300
|
readonly name: "remote-control";
|
|
289
301
|
readonly disposition: "excluded";
|
|
@@ -296,16 +308,19 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
296
308
|
readonly reason: "Remote Control is outside the local-only boundary.";
|
|
297
309
|
}, {
|
|
298
310
|
readonly name: "voice";
|
|
299
|
-
readonly disposition: "
|
|
311
|
+
readonly disposition: "deferred";
|
|
300
312
|
readonly visibility: "conditional";
|
|
313
|
+
readonly reason: "Conditional voice input is required parity work and is not implemented yet.";
|
|
301
314
|
}, {
|
|
302
315
|
readonly name: "think-back";
|
|
303
|
-
readonly disposition: "
|
|
316
|
+
readonly disposition: "deferred";
|
|
304
317
|
readonly visibility: "conditional";
|
|
318
|
+
readonly reason: "The conditional retrospective flow is required parity work and is not implemented yet.";
|
|
305
319
|
}, {
|
|
306
320
|
readonly name: "thinkback-play";
|
|
307
|
-
readonly disposition: "
|
|
321
|
+
readonly disposition: "deferred";
|
|
308
322
|
readonly visibility: "hidden";
|
|
323
|
+
readonly reason: "The hidden retrospective playback flow is required parity work and is not implemented yet.";
|
|
309
324
|
}, {
|
|
310
325
|
readonly name: "permissions";
|
|
311
326
|
readonly disposition: "included";
|
|
@@ -361,8 +376,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
361
376
|
readonly visibility: "conditional";
|
|
362
377
|
}, {
|
|
363
378
|
readonly name: "torch";
|
|
364
|
-
readonly disposition: "
|
|
379
|
+
readonly disposition: "deferred";
|
|
365
380
|
readonly visibility: "conditional";
|
|
381
|
+
readonly reason: "This source-gated behavior is required conditional parity work and is not implemented yet.";
|
|
366
382
|
}];
|
|
367
383
|
export declare const CLAUDE_2_1_208_COMMAND_BY_NAME: ReadonlyMap<string, ClaudeCommandInventoryEntry>;
|
|
368
384
|
//# sourceMappingURL=claude-command-inventory.d.ts.map
|
|
@@ -4,18 +4,28 @@
|
|
|
4
4
|
// disappearing from the parity inventory.
|
|
5
5
|
export const CLAUDE_2_1_208_COMMAND_INVENTORY = [
|
|
6
6
|
{ name: 'add-dir', disposition: 'included', visibility: 'visible' },
|
|
7
|
-
{
|
|
7
|
+
{
|
|
8
|
+
name: 'advisor',
|
|
9
|
+
disposition: 'deferred',
|
|
10
|
+
visibility: 'conditional',
|
|
11
|
+
reason: 'Conditional advice mode is required parity work and is not implemented yet.',
|
|
12
|
+
},
|
|
8
13
|
{ name: 'agents', disposition: 'included', visibility: 'visible' },
|
|
9
14
|
{ name: 'branch', disposition: 'included', visibility: 'visible' },
|
|
10
15
|
{ name: 'btw', disposition: 'included', visibility: 'visible' },
|
|
11
16
|
{
|
|
12
17
|
name: 'chrome',
|
|
13
|
-
disposition: '
|
|
18
|
+
disposition: 'deferred',
|
|
14
19
|
visibility: 'conditional',
|
|
15
|
-
reason: 'Chrome integration is
|
|
20
|
+
reason: 'The CLI-driven Chrome integration is required parity work and is not implemented yet.',
|
|
16
21
|
},
|
|
17
22
|
{ name: 'clear', disposition: 'included', visibility: 'visible' },
|
|
18
|
-
{
|
|
23
|
+
{
|
|
24
|
+
name: 'color',
|
|
25
|
+
disposition: 'deferred',
|
|
26
|
+
visibility: 'visible',
|
|
27
|
+
reason: 'The dedicated /color contract is required; /theme is not a substitute.',
|
|
28
|
+
},
|
|
19
29
|
{ name: 'compact', disposition: 'included', visibility: 'visible' },
|
|
20
30
|
{ name: 'config', disposition: 'included', visibility: 'visible' },
|
|
21
31
|
{ name: 'copy', disposition: 'included', visibility: 'visible' },
|
|
@@ -26,19 +36,39 @@ export const CLAUDE_2_1_208_COMMAND_INVENTORY = [
|
|
|
26
36
|
reason: 'Desktop handoff/import is outside the CLI-only product boundary.',
|
|
27
37
|
},
|
|
28
38
|
{ name: 'context', disposition: 'included', visibility: 'conditional' },
|
|
29
|
-
{
|
|
39
|
+
{
|
|
40
|
+
name: 'cost',
|
|
41
|
+
disposition: 'deferred',
|
|
42
|
+
visibility: 'conditional',
|
|
43
|
+
reason: 'The dedicated /cost contract is required; /status is not a substitute.',
|
|
44
|
+
},
|
|
30
45
|
{ name: 'diff', disposition: 'included', visibility: 'visible' },
|
|
31
|
-
{
|
|
46
|
+
{
|
|
47
|
+
name: 'doctor',
|
|
48
|
+
disposition: 'deferred',
|
|
49
|
+
visibility: 'conditional',
|
|
50
|
+
reason: 'Interactive /doctor is required; the top-level command is not a substitute.',
|
|
51
|
+
},
|
|
32
52
|
{ name: 'effort', disposition: 'included', visibility: 'visible' },
|
|
33
53
|
{ name: 'exit', disposition: 'included', visibility: 'visible' },
|
|
34
|
-
{
|
|
54
|
+
{
|
|
55
|
+
name: 'fast',
|
|
56
|
+
disposition: 'deferred',
|
|
57
|
+
visibility: 'conditional',
|
|
58
|
+
reason: 'The /fast state flow is required; model and effort controls are not a substitute.',
|
|
59
|
+
},
|
|
35
60
|
{
|
|
36
61
|
name: 'files',
|
|
37
62
|
disposition: 'excluded',
|
|
38
63
|
visibility: 'conditional',
|
|
39
64
|
reason: 'The source command is restricted to the internal Ant user type.',
|
|
40
65
|
},
|
|
41
|
-
{
|
|
66
|
+
{
|
|
67
|
+
name: 'heapdump',
|
|
68
|
+
disposition: 'deferred',
|
|
69
|
+
visibility: 'hidden',
|
|
70
|
+
reason: 'The hidden heap-diagnostic contract is required parity work and is not implemented yet.',
|
|
71
|
+
},
|
|
42
72
|
{ name: 'help', disposition: 'included', visibility: 'visible' },
|
|
43
73
|
{
|
|
44
74
|
name: 'ide',
|
|
@@ -89,7 +119,12 @@ export const CLAUDE_2_1_208_COMMAND_INVENTORY = [
|
|
|
89
119
|
reason: 'Remote-session URLs and QR codes are outside the local-only boundary.',
|
|
90
120
|
},
|
|
91
121
|
{ name: 'skills', disposition: 'included', visibility: 'visible' },
|
|
92
|
-
{
|
|
122
|
+
{
|
|
123
|
+
name: 'stats',
|
|
124
|
+
disposition: 'deferred',
|
|
125
|
+
visibility: 'visible',
|
|
126
|
+
reason: 'Historical usage statistics are required parity work and are not implemented yet.',
|
|
127
|
+
},
|
|
93
128
|
{ name: 'status', disposition: 'included', visibility: 'visible' },
|
|
94
129
|
{ name: 'statusline', disposition: 'included', visibility: 'visible' },
|
|
95
130
|
{
|
|
@@ -149,7 +184,12 @@ export const CLAUDE_2_1_208_COMMAND_INVENTORY = [
|
|
|
149
184
|
visibility: 'conditional',
|
|
150
185
|
reason: 'The source command is the Claude subscription plan-usage panel.',
|
|
151
186
|
},
|
|
152
|
-
{
|
|
187
|
+
{
|
|
188
|
+
name: 'insights',
|
|
189
|
+
disposition: 'deferred',
|
|
190
|
+
visibility: 'visible',
|
|
191
|
+
reason: 'Retrospective insights are required parity work and are not implemented yet.',
|
|
192
|
+
},
|
|
153
193
|
{ name: 'vim', disposition: 'included', visibility: 'visible' },
|
|
154
194
|
{
|
|
155
195
|
name: 'web-setup',
|
|
@@ -158,10 +198,30 @@ export const CLAUDE_2_1_208_COMMAND_INVENTORY = [
|
|
|
158
198
|
reason: 'Remote setup is outside the local-only boundary.',
|
|
159
199
|
},
|
|
160
200
|
{ name: 'fork', disposition: 'included', visibility: 'conditional' },
|
|
161
|
-
{
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
201
|
+
{
|
|
202
|
+
name: 'buddy',
|
|
203
|
+
disposition: 'deferred',
|
|
204
|
+
visibility: 'conditional',
|
|
205
|
+
reason: 'This conditional source mode is required parity work and is not implemented yet.',
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'proactive',
|
|
209
|
+
disposition: 'deferred',
|
|
210
|
+
visibility: 'conditional',
|
|
211
|
+
reason: 'This conditional source mode is required parity work and is not implemented yet.',
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: 'brief',
|
|
215
|
+
disposition: 'deferred',
|
|
216
|
+
visibility: 'conditional',
|
|
217
|
+
reason: 'This conditional source mode is required parity work and is not implemented yet.',
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: 'assistant',
|
|
221
|
+
disposition: 'deferred',
|
|
222
|
+
visibility: 'conditional',
|
|
223
|
+
reason: 'This conditional source mode is required parity work and is not implemented yet.',
|
|
224
|
+
},
|
|
165
225
|
{
|
|
166
226
|
name: 'remote-control',
|
|
167
227
|
disposition: 'excluded',
|
|
@@ -174,9 +234,24 @@ export const CLAUDE_2_1_208_COMMAND_INVENTORY = [
|
|
|
174
234
|
visibility: 'conditional',
|
|
175
235
|
reason: 'Remote Control is outside the local-only boundary.',
|
|
176
236
|
},
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
|
|
237
|
+
{
|
|
238
|
+
name: 'voice',
|
|
239
|
+
disposition: 'deferred',
|
|
240
|
+
visibility: 'conditional',
|
|
241
|
+
reason: 'Conditional voice input is required parity work and is not implemented yet.',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'think-back',
|
|
245
|
+
disposition: 'deferred',
|
|
246
|
+
visibility: 'conditional',
|
|
247
|
+
reason: 'The conditional retrospective flow is required parity work and is not implemented yet.',
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'thinkback-play',
|
|
251
|
+
disposition: 'deferred',
|
|
252
|
+
visibility: 'hidden',
|
|
253
|
+
reason: 'The hidden retrospective playback flow is required parity work and is not implemented yet.',
|
|
254
|
+
},
|
|
180
255
|
{ name: 'permissions', disposition: 'included', visibility: 'visible' },
|
|
181
256
|
{ name: 'plan', disposition: 'included', visibility: 'visible' },
|
|
182
257
|
{
|
|
@@ -214,7 +289,12 @@ export const CLAUDE_2_1_208_COMMAND_INVENTORY = [
|
|
|
214
289
|
},
|
|
215
290
|
{ name: 'tasks', disposition: 'included', visibility: 'visible' },
|
|
216
291
|
{ name: 'workflows', disposition: 'included', visibility: 'conditional' },
|
|
217
|
-
{
|
|
292
|
+
{
|
|
293
|
+
name: 'torch',
|
|
294
|
+
disposition: 'deferred',
|
|
295
|
+
visibility: 'conditional',
|
|
296
|
+
reason: 'This source-gated behavior is required conditional parity work and is not implemented yet.',
|
|
297
|
+
},
|
|
218
298
|
];
|
|
219
299
|
export const CLAUDE_2_1_208_COMMAND_BY_NAME = new Map(CLAUDE_2_1_208_COMMAND_INVENTORY.map((entry) => [entry.name, entry]));
|
|
220
300
|
//# sourceMappingURL=claude-command-inventory.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const CLAUDE_CHANGELOG_URL = "https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md";
|
|
2
|
+
export interface ReleaseNotesOptions {
|
|
3
|
+
configRoot: string;
|
|
4
|
+
fetcher?: typeof fetch;
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function parseClaudeChangelog(content: string): readonly [version: string, notes: readonly string[]][];
|
|
8
|
+
export declare function formatClaudeReleaseNotes(content: string): string | null;
|
|
9
|
+
export declare function loadClaudeReleaseNotes({ configRoot, fetcher, timeoutMs, }: ReleaseNotesOptions): Promise<string>;
|
|
10
|
+
//# sourceMappingURL=release-notes.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
export const CLAUDE_CHANGELOG_URL = 'https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md';
|
|
4
|
+
const CLAUDE_RAW_CHANGELOG_URL = 'https://raw.githubusercontent.com/anthropics/claude-code/refs/heads/main/CHANGELOG.md';
|
|
5
|
+
export function parseClaudeChangelog(content) {
|
|
6
|
+
const releases = [];
|
|
7
|
+
for (const section of content.split(/^## /gmu).slice(1)) {
|
|
8
|
+
const [heading, ...lines] = section.trim().split(/\r?\n/u);
|
|
9
|
+
const version = heading?.split(' - ')[0]?.trim();
|
|
10
|
+
if (!version)
|
|
11
|
+
continue;
|
|
12
|
+
const notes = lines
|
|
13
|
+
.map((line) => line.trim())
|
|
14
|
+
.filter((line) => line.startsWith('- '))
|
|
15
|
+
.map((line) => line.slice(2).trim())
|
|
16
|
+
.filter(Boolean);
|
|
17
|
+
if (notes.length > 0)
|
|
18
|
+
releases.push([version, notes]);
|
|
19
|
+
}
|
|
20
|
+
return releases.sort(([left], [right]) => left.localeCompare(right, undefined, { numeric: true }));
|
|
21
|
+
}
|
|
22
|
+
export function formatClaudeReleaseNotes(content) {
|
|
23
|
+
const releases = parseClaudeChangelog(content);
|
|
24
|
+
if (releases.length === 0)
|
|
25
|
+
return null;
|
|
26
|
+
return releases
|
|
27
|
+
.map(([version, notes]) => `Version ${version}:\n${notes.map((note) => `· ${note}`).join('\n')}`)
|
|
28
|
+
.join('\n\n');
|
|
29
|
+
}
|
|
30
|
+
async function cachedChangelog(path) {
|
|
31
|
+
try {
|
|
32
|
+
return await readFile(path, 'utf8');
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error.code === 'ENOENT')
|
|
36
|
+
return '';
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function loadClaudeReleaseNotes({ configRoot, fetcher = fetch, timeoutMs = 500, }) {
|
|
41
|
+
const cachePath = join(configRoot, 'cache', 'changelog.md');
|
|
42
|
+
let fresh = '';
|
|
43
|
+
try {
|
|
44
|
+
const response = await fetcher(CLAUDE_RAW_CHANGELOG_URL, {
|
|
45
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
46
|
+
});
|
|
47
|
+
if (response.ok) {
|
|
48
|
+
fresh = await response.text();
|
|
49
|
+
await mkdir(dirname(cachePath), { recursive: true });
|
|
50
|
+
await writeFile(cachePath, fresh, 'utf8');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// A release-notes lookup is best-effort; the shared cache is authoritative
|
|
55
|
+
// whenever the network is unavailable or slower than the UI budget.
|
|
56
|
+
}
|
|
57
|
+
const formatted = formatClaudeReleaseNotes(fresh || (await cachedChangelog(cachePath)));
|
|
58
|
+
return formatted ?? `See the full changelog at: ${CLAUDE_CHANGELOG_URL}`;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=release-notes.js.map
|
|
@@ -124,6 +124,11 @@ export const BUILTIN_TUI_SLASH_COMMANDS = [
|
|
|
124
124
|
description: 'Activate pending plugin changes in the current session',
|
|
125
125
|
source: 'builtin',
|
|
126
126
|
},
|
|
127
|
+
{
|
|
128
|
+
name: 'release-notes',
|
|
129
|
+
description: 'View release notes',
|
|
130
|
+
source: 'builtin',
|
|
131
|
+
},
|
|
127
132
|
{
|
|
128
133
|
name: 'rename',
|
|
129
134
|
description: 'Rename the current conversation',
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { ClaudeResourceScope } from '../../compatibility/claude/shared-resources.js';
|
|
2
|
+
import type { ModelUsage } from '../../core/runtime.js';
|
|
3
|
+
export interface ClaudeStatusLineSetting {
|
|
4
|
+
type: 'command';
|
|
5
|
+
command: string;
|
|
6
|
+
padding?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ClaudeStatusLineInput {
|
|
9
|
+
session_id: string;
|
|
10
|
+
session_name?: string;
|
|
11
|
+
transcript_path: string;
|
|
12
|
+
cwd: string;
|
|
13
|
+
permission_mode?: string;
|
|
14
|
+
model: {
|
|
15
|
+
id: string;
|
|
16
|
+
display_name: string;
|
|
17
|
+
};
|
|
18
|
+
workspace: {
|
|
19
|
+
current_dir: string;
|
|
20
|
+
project_dir: string;
|
|
21
|
+
added_dirs: readonly string[];
|
|
22
|
+
};
|
|
23
|
+
version: string;
|
|
24
|
+
output_style: {
|
|
25
|
+
name: string;
|
|
26
|
+
};
|
|
27
|
+
cost: {
|
|
28
|
+
total_cost_usd: number;
|
|
29
|
+
total_duration_ms: number;
|
|
30
|
+
total_api_duration_ms: number;
|
|
31
|
+
total_lines_added: number;
|
|
32
|
+
total_lines_removed: number;
|
|
33
|
+
};
|
|
34
|
+
context_window: {
|
|
35
|
+
total_input_tokens: number;
|
|
36
|
+
total_output_tokens: number;
|
|
37
|
+
context_window_size: number;
|
|
38
|
+
current_usage: {
|
|
39
|
+
input_tokens: number;
|
|
40
|
+
output_tokens: number;
|
|
41
|
+
cache_creation_input_tokens: number;
|
|
42
|
+
cache_read_input_tokens: number;
|
|
43
|
+
} | null;
|
|
44
|
+
used_percentage: number | null;
|
|
45
|
+
remaining_percentage: number | null;
|
|
46
|
+
};
|
|
47
|
+
exceeds_200k_tokens: boolean;
|
|
48
|
+
vim?: {
|
|
49
|
+
mode: 'INSERT' | 'NORMAL';
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export declare function loadClaudeStatusLineSetting(options: {
|
|
53
|
+
configRoot: string;
|
|
54
|
+
cwd: string;
|
|
55
|
+
settingSources?: readonly ClaudeResourceScope[];
|
|
56
|
+
}): Promise<{
|
|
57
|
+
setting?: ClaudeStatusLineSetting;
|
|
58
|
+
disabled: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
export declare function executeClaudeStatusLine(setting: ClaudeStatusLineSetting, input: ClaudeStatusLineInput, options?: {
|
|
61
|
+
cwd: string;
|
|
62
|
+
signal?: AbortSignal;
|
|
63
|
+
timeoutMs?: number;
|
|
64
|
+
}): Promise<string | undefined>;
|
|
65
|
+
export declare function createClaudeStatusLineInput(options: {
|
|
66
|
+
configRoot: string;
|
|
67
|
+
cwd: string;
|
|
68
|
+
projectDir: string;
|
|
69
|
+
sessionId: string;
|
|
70
|
+
sessionName?: string | null;
|
|
71
|
+
model?: string;
|
|
72
|
+
version: string;
|
|
73
|
+
outputStyle: string;
|
|
74
|
+
permissionMode?: string;
|
|
75
|
+
additionalDirectories: readonly string[];
|
|
76
|
+
usage?: ModelUsage;
|
|
77
|
+
costUsd?: number;
|
|
78
|
+
contextWindowTokens?: number;
|
|
79
|
+
vimMode?: 'INSERT' | 'NORMAL';
|
|
80
|
+
}): ClaudeStatusLineInput;
|
|
81
|
+
export declare function StatusLine({ configRoot, cwd, input, refreshKey, settingSources, }: {
|
|
82
|
+
configRoot: string;
|
|
83
|
+
cwd: string;
|
|
84
|
+
input: ClaudeStatusLineInput;
|
|
85
|
+
refreshKey: string;
|
|
86
|
+
settingSources?: readonly ClaudeResourceScope[];
|
|
87
|
+
}): import("react").JSX.Element | null;
|
|
88
|
+
//# sourceMappingURL=status-line.d.ts.map
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { watchFile, unwatchFile } from 'node:fs';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { Box, Text } from 'ink';
|
|
6
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
7
|
+
import { isClaudeSessionId, resolveClaudePaths, sanitizeClaudeProjectPath, } from '../../compatibility/claude/paths.js';
|
|
8
|
+
import { loadClaudeSettings } from '../../compatibility/claude/shared-resources.js';
|
|
9
|
+
import { commandShell, commandShellArguments, } from '../../platform/command-shell.js';
|
|
10
|
+
function record(value) {
|
|
11
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
12
|
+
? value
|
|
13
|
+
: null;
|
|
14
|
+
}
|
|
15
|
+
export async function loadClaudeStatusLineSetting(options) {
|
|
16
|
+
const resources = await loadClaudeSettings(options);
|
|
17
|
+
const merged = Object.assign({}, ...resources.map((resource) => record(resource.value) ?? {}));
|
|
18
|
+
const value = record(merged.statusLine);
|
|
19
|
+
const setting = value?.type === 'command' && typeof value.command === 'string'
|
|
20
|
+
? {
|
|
21
|
+
type: 'command',
|
|
22
|
+
command: value.command,
|
|
23
|
+
...(typeof value.padding === 'number' &&
|
|
24
|
+
Number.isFinite(value.padding)
|
|
25
|
+
? { padding: value.padding }
|
|
26
|
+
: {}),
|
|
27
|
+
}
|
|
28
|
+
: undefined;
|
|
29
|
+
return {
|
|
30
|
+
...(setting ? { setting } : {}),
|
|
31
|
+
disabled: merged.disableAllHooks === true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export async function executeClaudeStatusLine(setting, input, options = {
|
|
35
|
+
cwd: process.cwd(),
|
|
36
|
+
}) {
|
|
37
|
+
const timeout = AbortSignal.timeout(options.timeoutMs ?? 5000);
|
|
38
|
+
const signal = options.signal
|
|
39
|
+
? AbortSignal.any([options.signal, timeout])
|
|
40
|
+
: timeout;
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
let settled = false;
|
|
43
|
+
let stdout = '';
|
|
44
|
+
const finish = (value) => {
|
|
45
|
+
if (settled)
|
|
46
|
+
return;
|
|
47
|
+
settled = true;
|
|
48
|
+
resolve(value);
|
|
49
|
+
};
|
|
50
|
+
let child;
|
|
51
|
+
try {
|
|
52
|
+
child = spawn(commandShell(), commandShellArguments(setting.command), {
|
|
53
|
+
cwd: options.cwd,
|
|
54
|
+
detached: process.platform !== 'win32',
|
|
55
|
+
env: process.env,
|
|
56
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
finish();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
child.stdout.setEncoding('utf8');
|
|
64
|
+
const terminate = () => {
|
|
65
|
+
if (child.pid === undefined)
|
|
66
|
+
return;
|
|
67
|
+
try {
|
|
68
|
+
if (process.platform === 'win32')
|
|
69
|
+
child.kill('SIGKILL');
|
|
70
|
+
else
|
|
71
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error.code !== 'ESRCH')
|
|
75
|
+
finish();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
if (signal.aborted)
|
|
79
|
+
terminate();
|
|
80
|
+
else
|
|
81
|
+
signal.addEventListener('abort', terminate, { once: true });
|
|
82
|
+
child.stdout.on('data', (chunk) => {
|
|
83
|
+
if (stdout.length <= 1024 * 1024)
|
|
84
|
+
stdout += chunk;
|
|
85
|
+
});
|
|
86
|
+
child.on('error', () => {
|
|
87
|
+
signal.removeEventListener('abort', terminate);
|
|
88
|
+
finish();
|
|
89
|
+
});
|
|
90
|
+
child.on('close', (code) => {
|
|
91
|
+
signal.removeEventListener('abort', terminate);
|
|
92
|
+
if (code !== 0 || signal.aborted) {
|
|
93
|
+
finish();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const output = stdout
|
|
97
|
+
.trim()
|
|
98
|
+
.split(/\r?\n/u)
|
|
99
|
+
.map((line) => line.trim())
|
|
100
|
+
.filter(Boolean)
|
|
101
|
+
.join('\n');
|
|
102
|
+
finish(output || undefined);
|
|
103
|
+
});
|
|
104
|
+
child.stdin.on('error', () => undefined);
|
|
105
|
+
child.stdin.end(JSON.stringify(input));
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
export function createClaudeStatusLineInput(options) {
|
|
109
|
+
const usage = options.usage;
|
|
110
|
+
const contextInputTokens = usage
|
|
111
|
+
? usage.inputTokens +
|
|
112
|
+
(usage.cacheReadInputTokens ?? 0) +
|
|
113
|
+
(usage.cacheCreationInputTokens ?? 0)
|
|
114
|
+
: null;
|
|
115
|
+
const usedTokens = contextInputTokens === null || usage === undefined
|
|
116
|
+
? null
|
|
117
|
+
: contextInputTokens + usage.outputTokens;
|
|
118
|
+
const contextWindowSize = options.contextWindowTokens ?? 0;
|
|
119
|
+
const usedPercentage = contextInputTokens === null || contextWindowSize <= 0
|
|
120
|
+
? null
|
|
121
|
+
: Math.min(100, Math.max(0, Math.round((contextInputTokens / contextWindowSize) * 100)));
|
|
122
|
+
const model = options.model ?? 'default';
|
|
123
|
+
const transcriptPath = isClaudeSessionId(options.sessionId)
|
|
124
|
+
? resolveClaudePaths({
|
|
125
|
+
configDir: options.configRoot,
|
|
126
|
+
cwd: options.cwd,
|
|
127
|
+
sessionId: options.sessionId,
|
|
128
|
+
}).sessionFile
|
|
129
|
+
: resolve(options.configRoot, 'projects', sanitizeClaudeProjectPath(options.cwd), `${options.sessionId}.jsonl`);
|
|
130
|
+
return {
|
|
131
|
+
session_id: options.sessionId,
|
|
132
|
+
...(options.sessionName ? { session_name: options.sessionName } : {}),
|
|
133
|
+
transcript_path: transcriptPath,
|
|
134
|
+
cwd: options.cwd,
|
|
135
|
+
...(options.permissionMode
|
|
136
|
+
? { permission_mode: options.permissionMode }
|
|
137
|
+
: {}),
|
|
138
|
+
model: { id: model, display_name: model },
|
|
139
|
+
workspace: {
|
|
140
|
+
current_dir: options.cwd,
|
|
141
|
+
project_dir: options.projectDir,
|
|
142
|
+
added_dirs: options.additionalDirectories,
|
|
143
|
+
},
|
|
144
|
+
version: options.version,
|
|
145
|
+
output_style: { name: options.outputStyle },
|
|
146
|
+
cost: {
|
|
147
|
+
total_cost_usd: options.costUsd ?? 0,
|
|
148
|
+
total_duration_ms: 0,
|
|
149
|
+
total_api_duration_ms: 0,
|
|
150
|
+
total_lines_added: 0,
|
|
151
|
+
total_lines_removed: 0,
|
|
152
|
+
},
|
|
153
|
+
context_window: {
|
|
154
|
+
total_input_tokens: usage?.inputTokens ?? 0,
|
|
155
|
+
total_output_tokens: usage?.outputTokens ?? 0,
|
|
156
|
+
context_window_size: contextWindowSize,
|
|
157
|
+
current_usage: usage
|
|
158
|
+
? {
|
|
159
|
+
input_tokens: usage.inputTokens,
|
|
160
|
+
output_tokens: usage.outputTokens,
|
|
161
|
+
cache_creation_input_tokens: usage.cacheCreationInputTokens ?? 0,
|
|
162
|
+
cache_read_input_tokens: usage.cacheReadInputTokens ?? 0,
|
|
163
|
+
}
|
|
164
|
+
: null,
|
|
165
|
+
used_percentage: usedPercentage,
|
|
166
|
+
remaining_percentage: usedPercentage === null ? null : 100 - usedPercentage,
|
|
167
|
+
},
|
|
168
|
+
exceeds_200k_tokens: usedTokens !== null && usedTokens > 200_000,
|
|
169
|
+
...(options.vimMode ? { vim: { mode: options.vimMode } } : {}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export function StatusLine({ configRoot, cwd, input, refreshKey, settingSources, }) {
|
|
173
|
+
const [text, setText] = useState();
|
|
174
|
+
const [padding, setPadding] = useState(0);
|
|
175
|
+
const timer = useRef(undefined);
|
|
176
|
+
const controller = useRef(undefined);
|
|
177
|
+
const latestInput = useRef(input);
|
|
178
|
+
latestInput.current = input;
|
|
179
|
+
const update = useCallback(async () => {
|
|
180
|
+
controller.current?.abort();
|
|
181
|
+
const current = new AbortController();
|
|
182
|
+
controller.current = current;
|
|
183
|
+
try {
|
|
184
|
+
const loaded = await loadClaudeStatusLineSetting({
|
|
185
|
+
configRoot,
|
|
186
|
+
cwd,
|
|
187
|
+
...(settingSources === undefined ? {} : { settingSources }),
|
|
188
|
+
});
|
|
189
|
+
if (current.signal.aborted)
|
|
190
|
+
return;
|
|
191
|
+
setPadding(loaded.setting?.padding ?? 0);
|
|
192
|
+
if (loaded.disabled || !loaded.setting) {
|
|
193
|
+
setText(undefined);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const result = await executeClaudeStatusLine(loaded.setting, latestInput.current, { cwd, signal: current.signal });
|
|
197
|
+
if (!current.signal.aborted)
|
|
198
|
+
setText(result);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
if (!current.signal.aborted)
|
|
202
|
+
setText(undefined);
|
|
203
|
+
}
|
|
204
|
+
}, [configRoot, cwd, settingSources]);
|
|
205
|
+
const schedule = useCallback(() => {
|
|
206
|
+
if (timer.current)
|
|
207
|
+
clearTimeout(timer.current);
|
|
208
|
+
timer.current = setTimeout(() => void update(), 300);
|
|
209
|
+
}, [update]);
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
schedule();
|
|
212
|
+
}, [refreshKey, schedule]);
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
const paths = [
|
|
215
|
+
join(configRoot, 'settings.json'),
|
|
216
|
+
join(cwd, '.claude', 'settings.json'),
|
|
217
|
+
join(cwd, '.claude', 'settings.local.json'),
|
|
218
|
+
];
|
|
219
|
+
const changed = () => schedule();
|
|
220
|
+
for (const path of paths)
|
|
221
|
+
watchFile(path, { interval: 500 }, changed);
|
|
222
|
+
return () => {
|
|
223
|
+
for (const path of paths)
|
|
224
|
+
unwatchFile(path, changed);
|
|
225
|
+
controller.current?.abort();
|
|
226
|
+
if (timer.current)
|
|
227
|
+
clearTimeout(timer.current);
|
|
228
|
+
};
|
|
229
|
+
}, [configRoot, cwd, schedule]);
|
|
230
|
+
if (!text)
|
|
231
|
+
return null;
|
|
232
|
+
return (_jsx(Box, { paddingX: padding, children: _jsx(Text, { dimColor: true, wrap: "truncate", children: text }) }));
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=status-line.js.map
|
package/dist/cli.d.ts
CHANGED
|
@@ -96,6 +96,7 @@ interface TopLevelAgentCommands {
|
|
|
96
96
|
attach(id: string, input: AsyncIterable<string | Uint8Array>, output: (text: string) => void, signal?: AbortSignal): Promise<void>;
|
|
97
97
|
}
|
|
98
98
|
export interface CliDependencies extends InteractiveServiceFactory {
|
|
99
|
+
loadReleaseNotes?(configRoot: string): Promise<string>;
|
|
99
100
|
createService(options: {
|
|
100
101
|
eventSink: RuntimeEventSink;
|
|
101
102
|
requireProvider: boolean;
|
package/dist/cli.js
CHANGED
|
@@ -14,6 +14,7 @@ import { isClaudeSessionId, resolveClaudePaths, } from './compatibility/claude/p
|
|
|
14
14
|
import { loadClaudeContextResources, loadClaudeSettings, loadClaudeSharedResources, resolveClaudeProjectMemoryDirectory, } from './compatibility/claude/shared-resources.js';
|
|
15
15
|
import { AgentRunCancelledError, } from './core/runtime.js';
|
|
16
16
|
import { persistTuiPermissionUpdates } from './cli/tui/permission-settings.js';
|
|
17
|
+
import { loadClaudeReleaseNotes } from './cli/tui/release-notes.js';
|
|
17
18
|
import { projectTuiHooks, } from './cli/tui/hook-settings.js';
|
|
18
19
|
import { DEFAULT_CLI_CONTROLS, resolveCliControls } from './cli/controls.js';
|
|
19
20
|
import { applyRuntimeSettingDefaults, loadRuntimeSettings, runtimeSettingsSystemPrompt, } from './cli/tui/runtime-settings.js';
|
|
@@ -1609,6 +1610,9 @@ const defaultDependencies = {
|
|
|
1609
1610
|
? { allowDangerouslySkipPermissions: true }
|
|
1610
1611
|
: {}),
|
|
1611
1612
|
additionalDirectories: initialAdditionalDirectories,
|
|
1613
|
+
...(interactiveControls.settingSources === undefined
|
|
1614
|
+
? {}
|
|
1615
|
+
: { settingSources: interactiveControls.settingSources }),
|
|
1612
1616
|
display: {
|
|
1613
1617
|
version: VERSION,
|
|
1614
1618
|
cwd: process.cwd(),
|
|
@@ -3856,6 +3860,48 @@ async function execute(argv, io, dependencies, signal) {
|
|
|
3856
3860
|
: {}),
|
|
3857
3861
|
};
|
|
3858
3862
|
};
|
|
3863
|
+
const headlessPromptArgs = command === 'resume' ? args.slice(2) : knownCommand ? args.slice(1) : args;
|
|
3864
|
+
const headlessPrompt = inputFormat === 'text' && headlessPromptArgs.length > 0
|
|
3865
|
+
? promptFrom(headlessPromptArgs)
|
|
3866
|
+
: undefined;
|
|
3867
|
+
if (headlessPrompt === '/release-notes' && !invocation.disableSlashCommands) {
|
|
3868
|
+
const startedAt = Date.now();
|
|
3869
|
+
const sessionId = invocation.sessionId ?? randomUUID();
|
|
3870
|
+
const configRoot = resolve(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.claude'));
|
|
3871
|
+
const text = dependencies.loadReleaseNotes
|
|
3872
|
+
? await dependencies.loadReleaseNotes(configRoot)
|
|
3873
|
+
: await loadClaudeReleaseNotes({ configRoot });
|
|
3874
|
+
const result = {
|
|
3875
|
+
sessionId,
|
|
3876
|
+
text,
|
|
3877
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
3878
|
+
};
|
|
3879
|
+
const info = {
|
|
3880
|
+
cwd: process.cwd(),
|
|
3881
|
+
model: invocation.model ?? process.env.PRAXIS_MODEL ?? 'unknown',
|
|
3882
|
+
tools: [],
|
|
3883
|
+
mcpServers: [],
|
|
3884
|
+
permissionMode: invocation.permissionMode,
|
|
3885
|
+
slashCommands: ['release-notes'],
|
|
3886
|
+
agents: [],
|
|
3887
|
+
skills: [],
|
|
3888
|
+
claudeCodeVersion: '2.1.208',
|
|
3889
|
+
};
|
|
3890
|
+
if (outputFormat === 'stream-json') {
|
|
3891
|
+
const output = new StreamJsonOutput((value) => writeJson(io, value), info, sessionId, includePartialMessages, invocation.includeHookEvents);
|
|
3892
|
+
output.init();
|
|
3893
|
+
output.sink({ type: 'text-delta', delta: text });
|
|
3894
|
+
output.sink({ type: 'usage', usage: result.usage });
|
|
3895
|
+
output.result(result, startedAt);
|
|
3896
|
+
}
|
|
3897
|
+
else if (outputFormat === 'json' || invocation.legacyJson) {
|
|
3898
|
+
writeJson(io, createSuccessResult(result, info, startedAt, 0));
|
|
3899
|
+
}
|
|
3900
|
+
else {
|
|
3901
|
+
io.stdout(`${text}\n`);
|
|
3902
|
+
}
|
|
3903
|
+
return 0;
|
|
3904
|
+
}
|
|
3859
3905
|
const service = await dependencies.createService({
|
|
3860
3906
|
eventSink: outputFormat === 'stream-json' && !invocation.legacyJson
|
|
3861
3907
|
? (event) => {
|
|
@@ -28,6 +28,7 @@ export interface ClaudePromptExpansionMessage {
|
|
|
28
28
|
contentBlocks?: readonly ModelContentBlock[];
|
|
29
29
|
images?: readonly ModelImage[];
|
|
30
30
|
}
|
|
31
|
+
export declare const BUILTIN_STATUSLINE_AGENT_PATH = "/__praxis_builtin__/agents/statusline-setup.md";
|
|
31
32
|
export declare function validateClaudeExtensions(resources: readonly ClaudeTextResource[]): void;
|
|
32
33
|
export declare class ClaudeExtensionCatalog {
|
|
33
34
|
private readonly commands;
|
|
@@ -34,6 +34,49 @@ Call CronCreate with the derived cron, the parsed prompt verbatim, and recurring
|
|
|
34
34
|
Input:
|
|
35
35
|
$ARGUMENTS`,
|
|
36
36
|
};
|
|
37
|
+
const BUILTIN_STATUSLINE_COMMAND = {
|
|
38
|
+
path: '/__praxis_builtin__/commands/statusline.md',
|
|
39
|
+
scope: 'user',
|
|
40
|
+
content: '',
|
|
41
|
+
kind: 'command',
|
|
42
|
+
name: 'statusline',
|
|
43
|
+
description: "Set up Claude Code's status line UI",
|
|
44
|
+
modelInvocable: true,
|
|
45
|
+
permissionSafe: true,
|
|
46
|
+
body: `Create an Agent with subagent_type "statusline-setup" and the prompt "$ARGUMENTS"`,
|
|
47
|
+
};
|
|
48
|
+
export const BUILTIN_STATUSLINE_AGENT_PATH = '/__praxis_builtin__/agents/statusline-setup.md';
|
|
49
|
+
const BUILTIN_STATUSLINE_AGENT = {
|
|
50
|
+
path: BUILTIN_STATUSLINE_AGENT_PATH,
|
|
51
|
+
scope: 'user',
|
|
52
|
+
content: '',
|
|
53
|
+
kind: 'agent',
|
|
54
|
+
name: 'statusline-setup',
|
|
55
|
+
description: "Configure the user's Claude Code status line setting.",
|
|
56
|
+
modelInvocable: false,
|
|
57
|
+
permissionSafe: false,
|
|
58
|
+
body: `You configure the statusLine command in the user's shared Claude Code settings.
|
|
59
|
+
|
|
60
|
+
Importing a shell prompt:
|
|
61
|
+
1. Inspect shell configuration in this order: ~/.zshrc, ~/.bashrc, ~/.bash_profile, then ~/.profile.
|
|
62
|
+
2. Find an exported or unexported quoted PS1 assignment. Preserve the prompt's information and colors.
|
|
63
|
+
3. Translate prompt escapes into commands: user to $(whoami), short/full host to $(hostname -s)/$(hostname), working directory to $(pwd), basename to $(basename "$(pwd)"), and time/date escapes to the corresponding date command. Preserve newlines and literal prompt counters.
|
|
64
|
+
4. Emit ANSI sequences with printf because the result is rendered by a terminal in dimmed colors.
|
|
65
|
+
5. Remove a trailing prompt marker such as "$" or ">" from the displayed status line.
|
|
66
|
+
6. If no PS1 exists and the request gives no other design, ask what information should be displayed.
|
|
67
|
+
|
|
68
|
+
The status command receives one JSON document on stdin. It contains session_id, optional session_name, transcript_path, cwd, model.id and model.display_name, workspace.current_dir/project_dir/added_dirs, version, output_style.name, cost totals, context_window totals/current_usage/used_percentage/remaining_percentage, exceeds_200k_tokens, and optional vim, agent, and worktree objects. Read stdin once and reuse it when extracting multiple values. Typical jq selectors include .model.display_name, .workspace.current_dir, .output_style.name, .context_window.remaining_percentage, and .context_window.used_percentage. Optional values must be tested before printing them.
|
|
69
|
+
|
|
70
|
+
Preserve every unrelated setting and update ~/.claude/settings.json with this shape:
|
|
71
|
+
{
|
|
72
|
+
"statusLine": {
|
|
73
|
+
"type": "command",
|
|
74
|
+
"command": "the command"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Resolve a symlinked settings file and edit its target. Complex implementations may live in a script under ~/.claude and be referenced by the setting. Git commands in that script must avoid optional locks. Report the exact configuration and script path, if any. End by telling the parent agent that future status-line changes must use the statusline-setup agent and that the user can request further changes.`,
|
|
79
|
+
};
|
|
37
80
|
const GENERAL_PURPOSE_AGENT = {
|
|
38
81
|
name: 'general-purpose',
|
|
39
82
|
description: 'General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks.',
|
|
@@ -175,7 +218,10 @@ export class ClaudeExtensionCatalog {
|
|
|
175
218
|
this.disableSlashCommands = options.disableSlashCommands === true;
|
|
176
219
|
this.commands = options.disableSlashCommands
|
|
177
220
|
? new Map()
|
|
178
|
-
: new Map([
|
|
221
|
+
: new Map([
|
|
222
|
+
['loop', BUILTIN_LOOP],
|
|
223
|
+
['statusline', BUILTIN_STATUSLINE_COMMAND],
|
|
224
|
+
]);
|
|
179
225
|
if (!options.disableSlashCommands) {
|
|
180
226
|
for (const [name, command] of indexed('command', resources.commands)) {
|
|
181
227
|
this.commands.set(name, command);
|
|
@@ -184,7 +230,10 @@ export class ClaudeExtensionCatalog {
|
|
|
184
230
|
this.skills = options.disableSlashCommands
|
|
185
231
|
? new Map()
|
|
186
232
|
: indexed('skill', resources.skills);
|
|
187
|
-
this.agents =
|
|
233
|
+
this.agents = new Map([
|
|
234
|
+
['statusline-setup', BUILTIN_STATUSLINE_AGENT],
|
|
235
|
+
...indexed('agent', resources.agents),
|
|
236
|
+
]);
|
|
188
237
|
}
|
|
189
238
|
setMcpPrompts(prompts) {
|
|
190
239
|
this.mcpPrompts.clear();
|
|
@@ -208,6 +257,9 @@ export class ClaudeExtensionCatalog {
|
|
|
208
257
|
if (!definition) {
|
|
209
258
|
return { userMessages: [prompt] };
|
|
210
259
|
}
|
|
260
|
+
const invocationArguments = definition === BUILTIN_STATUSLINE_COMMAND && argumentsText.length === 0
|
|
261
|
+
? 'Configure my statusLine from my shell PS1 configuration'
|
|
262
|
+
: argumentsText;
|
|
211
263
|
return {
|
|
212
264
|
userMessages: [
|
|
213
265
|
[
|
|
@@ -217,7 +269,7 @@ export class ClaudeExtensionCatalog {
|
|
|
217
269
|
? [`<command-args>${argumentsText}</command-args>`]
|
|
218
270
|
: []),
|
|
219
271
|
].join('\n'),
|
|
220
|
-
renderInvocation(definition,
|
|
272
|
+
renderInvocation(definition, invocationArguments),
|
|
221
273
|
],
|
|
222
274
|
};
|
|
223
275
|
}
|