1261 lines
32 KiB
TypeScript
1261 lines
32 KiB
TypeScript
import type {
|
|
BrowserSlashCommandDispatchResult,
|
|
BrowserSlashCommandSurface,
|
|
} from "./browser-slash-command-dispatch";
|
|
import type {
|
|
DoctorFixResult,
|
|
DoctorReport,
|
|
ForensicReport,
|
|
SkillHealthReport,
|
|
} from "./diagnostics-types";
|
|
import type {
|
|
KnowledgeData,
|
|
CapturesData,
|
|
CaptureResolveResult,
|
|
} from "./knowledge-captures-types";
|
|
import type { SettingsData } from "./settings-types";
|
|
import type {
|
|
HistoryData,
|
|
InspectData,
|
|
HooksData,
|
|
ExportResult,
|
|
UndoInfo,
|
|
CleanupData,
|
|
SteerData,
|
|
} from "./remaining-command-types";
|
|
import type { GitSummaryResponse } from "./git-summary-contract";
|
|
import type {
|
|
SessionBrowserNameFilter,
|
|
SessionBrowserSession,
|
|
SessionBrowserSortMode,
|
|
} from "./session-browser-contract";
|
|
|
|
export const COMMAND_SURFACE_THINKING_LEVELS = [
|
|
"off",
|
|
"minimal",
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"xhigh",
|
|
] as const;
|
|
|
|
export type CommandSurfaceThinkingLevel =
|
|
(typeof COMMAND_SURFACE_THINKING_LEVELS)[number];
|
|
export type CommandSurfaceSection =
|
|
| "general"
|
|
| "model"
|
|
| "thinking"
|
|
| "queue"
|
|
| "compaction"
|
|
| "retry"
|
|
| "session-behavior"
|
|
| "recovery"
|
|
| "auth"
|
|
| "admin"
|
|
| "git"
|
|
| "resume"
|
|
| "name"
|
|
| "fork"
|
|
| "session"
|
|
| "compact"
|
|
| "workspace"
|
|
| "integrations"
|
|
| "experimental"
|
|
// SF subcommand surfaces (S02)
|
|
| "sf-status"
|
|
| "sf-visualize"
|
|
| "sf-forensics"
|
|
| "sf-doctor"
|
|
| "sf-skill-health"
|
|
| "sf-knowledge"
|
|
| "sf-capture"
|
|
| "sf-triage"
|
|
| "sf-quick"
|
|
| "sf-history"
|
|
| "sf-undo"
|
|
| "sf-inspect"
|
|
| "sf-prefs"
|
|
| "sf-config"
|
|
| "sf-hooks"
|
|
| "sf-mode"
|
|
| "sf-steer"
|
|
| "sf-export"
|
|
| "sf-cleanup"
|
|
| "sf-queue";
|
|
export type CommandSurfaceSource = "slash" | "sidebar" | "surface";
|
|
export type CommandSurfacePendingAction =
|
|
| "loading_models"
|
|
| "set_model"
|
|
| "set_thinking_level"
|
|
| "set_steering_mode"
|
|
| "set_follow_up_mode"
|
|
| "set_auto_compaction"
|
|
| "set_auto_retry"
|
|
| "abort_retry"
|
|
| "load_git_summary"
|
|
| "load_recovery_diagnostics"
|
|
| "load_session_browser"
|
|
| "rename_session"
|
|
| "save_api_key"
|
|
| "start_provider_flow"
|
|
| "submit_provider_flow_input"
|
|
| "cancel_provider_flow"
|
|
| "logout_provider"
|
|
| "switch_session"
|
|
| "load_fork_messages"
|
|
| "fork_session"
|
|
| "load_session_stats"
|
|
| "export_html"
|
|
| "compact_session";
|
|
|
|
export interface CommandSurfaceModelOption {
|
|
provider: string;
|
|
modelId: string;
|
|
name?: string;
|
|
reasoning: boolean;
|
|
isCurrent: boolean;
|
|
}
|
|
|
|
export interface CommandSurfaceForkMessage {
|
|
entryId: string;
|
|
text: string;
|
|
}
|
|
|
|
export interface CommandSurfaceSessionStats {
|
|
sessionFile: string | undefined;
|
|
sessionId: string;
|
|
userMessages: number;
|
|
assistantMessages: number;
|
|
toolCalls: number;
|
|
toolResults: number;
|
|
totalMessages: number;
|
|
tokens: {
|
|
input: number;
|
|
output: number;
|
|
cacheRead: number;
|
|
cacheWrite: number;
|
|
total: number;
|
|
};
|
|
cost: number;
|
|
}
|
|
|
|
export interface CommandSurfaceCompactionResult {
|
|
summary: string;
|
|
firstKeptEntryId: string;
|
|
tokensBefore: number;
|
|
details?: unknown;
|
|
}
|
|
|
|
export interface CommandSurfaceResumableSession {
|
|
id: string;
|
|
path: string;
|
|
name?: string;
|
|
isActive: boolean;
|
|
}
|
|
|
|
export interface CommandSurfaceSessionBrowserState {
|
|
scope: "current_project" | null;
|
|
projectCwd: string | null;
|
|
projectSessionsDir: string | null;
|
|
activeSessionPath: string | null;
|
|
query: string;
|
|
sortMode: SessionBrowserSortMode;
|
|
nameFilter: SessionBrowserNameFilter;
|
|
totalSessions: number;
|
|
returnedSessions: number;
|
|
sessions: SessionBrowserSession[];
|
|
loaded: boolean;
|
|
error: string | null;
|
|
}
|
|
|
|
export interface CommandSurfaceSessionMutationState {
|
|
pending: boolean;
|
|
sessionPath: string | null;
|
|
result: string | null;
|
|
error: string | null;
|
|
}
|
|
|
|
export interface CommandSurfaceSettingMutationState {
|
|
pending: boolean;
|
|
result: string | null;
|
|
error: string | null;
|
|
}
|
|
|
|
export interface CommandSurfaceSettingsMutationState {
|
|
steeringMode: CommandSurfaceSettingMutationState;
|
|
followUpMode: CommandSurfaceSettingMutationState;
|
|
autoCompaction: CommandSurfaceSettingMutationState;
|
|
autoRetry: CommandSurfaceSettingMutationState;
|
|
abortRetry: CommandSurfaceSettingMutationState;
|
|
}
|
|
|
|
export interface CommandSurfaceGitSummaryState {
|
|
pending: boolean;
|
|
loaded: boolean;
|
|
result: GitSummaryResponse | null;
|
|
error: string | null;
|
|
}
|
|
|
|
export type WorkspaceRecoverySummaryTone = "healthy" | "warning" | "danger";
|
|
export type WorkspaceRecoveryDiagnosticsStatus = "ready" | "unavailable";
|
|
export type WorkspaceRecoveryBrowserActionId =
|
|
| "refresh_diagnostics"
|
|
| "refresh_workspace"
|
|
| "open_retry_controls"
|
|
| "open_resume_controls"
|
|
| "open_auth_controls";
|
|
export type CommandSurfaceRecoveryPhase =
|
|
| "idle"
|
|
| "loading"
|
|
| "ready"
|
|
| "unavailable"
|
|
| "error";
|
|
|
|
export interface WorkspaceRecoveryBrowserAction {
|
|
id: WorkspaceRecoveryBrowserActionId;
|
|
label: string;
|
|
detail: string;
|
|
emphasis?: "primary" | "secondary" | "danger";
|
|
}
|
|
|
|
export interface WorkspaceRecoveryCommandSuggestion {
|
|
label: string;
|
|
command: string;
|
|
}
|
|
|
|
export interface WorkspaceRecoveryCodeSummary {
|
|
code: string;
|
|
count: number;
|
|
label: string;
|
|
severity: "info" | "warning" | "error";
|
|
}
|
|
|
|
export interface WorkspaceRecoveryIssueDigest {
|
|
code: string;
|
|
severity: "info" | "warning" | "error";
|
|
scope: string;
|
|
message: string;
|
|
file?: string;
|
|
suggestion?: string;
|
|
unitId?: string;
|
|
}
|
|
|
|
export interface WorkspaceRecoveryDiagnostics {
|
|
status: WorkspaceRecoveryDiagnosticsStatus;
|
|
loadedAt: string;
|
|
project: {
|
|
cwd: string;
|
|
activeScope: string | null;
|
|
activeSessionPath: string | null;
|
|
activeSessionId: string | null;
|
|
};
|
|
summary: {
|
|
tone: WorkspaceRecoverySummaryTone;
|
|
label: string;
|
|
detail: string;
|
|
validationCount: number;
|
|
doctorIssueCount: number;
|
|
lastFailurePhase: string | null;
|
|
currentUnitId: string | null;
|
|
retryAttempt: number;
|
|
retryInProgress: boolean;
|
|
compactionActive: boolean;
|
|
};
|
|
bridge: {
|
|
phase: string;
|
|
retry: {
|
|
enabled: boolean;
|
|
inProgress: boolean;
|
|
attempt: number;
|
|
label: string;
|
|
};
|
|
compaction: {
|
|
active: boolean;
|
|
label: string;
|
|
};
|
|
lastFailure: {
|
|
message: string;
|
|
phase: string;
|
|
at: string;
|
|
commandType: string | null;
|
|
afterSessionAttachment: boolean;
|
|
} | null;
|
|
authRefresh: {
|
|
phase: string;
|
|
error: string | null;
|
|
label: string;
|
|
};
|
|
};
|
|
validation: {
|
|
total: number;
|
|
bySeverity: {
|
|
errors: number;
|
|
warnings: number;
|
|
infos: number;
|
|
};
|
|
codes: WorkspaceRecoveryCodeSummary[];
|
|
topIssues: WorkspaceRecoveryIssueDigest[];
|
|
};
|
|
doctor: {
|
|
scope: string | null;
|
|
total: number;
|
|
errors: number;
|
|
warnings: number;
|
|
infos: number;
|
|
fixable: number;
|
|
codes: Array<{ code: string; count: number }>;
|
|
topIssues: WorkspaceRecoveryIssueDigest[];
|
|
};
|
|
interruptedRun: {
|
|
available: boolean;
|
|
detected: boolean;
|
|
label: string;
|
|
detail: string;
|
|
unit: {
|
|
type: string;
|
|
id: string;
|
|
} | null;
|
|
counts: {
|
|
toolCalls: number;
|
|
filesWritten: number;
|
|
commandsRun: number;
|
|
errors: number;
|
|
};
|
|
gitChangesDetected: boolean;
|
|
lastError: string | null;
|
|
};
|
|
actions: {
|
|
browser: WorkspaceRecoveryBrowserAction[];
|
|
commands: WorkspaceRecoveryCommandSuggestion[];
|
|
};
|
|
}
|
|
|
|
export interface CommandSurfaceRecoveryState {
|
|
phase: CommandSurfaceRecoveryPhase;
|
|
pending: boolean;
|
|
loaded: boolean;
|
|
stale: boolean;
|
|
diagnostics: WorkspaceRecoveryDiagnostics | null;
|
|
error: string | null;
|
|
lastLoadedAt: string | null;
|
|
lastInvalidatedAt: string | null;
|
|
lastFailureAt: string | null;
|
|
}
|
|
|
|
export interface WorkspaceRecoverySummary {
|
|
visible: boolean;
|
|
tone: WorkspaceRecoverySummaryTone;
|
|
label: string;
|
|
detail: string;
|
|
validationCount: number;
|
|
retryInProgress: boolean;
|
|
retryAttempt: number;
|
|
autoRetryEnabled: boolean;
|
|
isCompacting: boolean;
|
|
currentUnitId: string | null;
|
|
freshness: "idle" | "fresh" | "stale" | "error";
|
|
entrypointLabel: string;
|
|
lastError: {
|
|
message: string;
|
|
phase: string;
|
|
at: string;
|
|
} | null;
|
|
}
|
|
|
|
export type CommandSurfaceTarget =
|
|
| { kind: "settings"; section: CommandSurfaceSection }
|
|
| { kind: "model"; provider?: string; modelId?: string; query?: string }
|
|
| { kind: "thinking"; level: CommandSurfaceThinkingLevel }
|
|
| { kind: "auth"; providerId?: string; intent: "login" | "logout" | "manage" }
|
|
| { kind: "resume"; sessionPath?: string }
|
|
| { kind: "name"; sessionPath?: string; name: string }
|
|
| { kind: "fork"; entryId?: string }
|
|
| { kind: "session"; outputPath?: string }
|
|
| { kind: "compact"; customInstructions: string }
|
|
| { kind: "sf"; surface: string; subcommand: string; args: string };
|
|
|
|
// ─── Diagnostics panel state ──────────────────────────────────────────────────
|
|
|
|
export type CommandSurfaceDiagnosticsPhase =
|
|
| "idle"
|
|
| "loading"
|
|
| "loaded"
|
|
| "error";
|
|
|
|
export interface CommandSurfaceDiagnosticsPhaseState<T> {
|
|
phase: CommandSurfaceDiagnosticsPhase;
|
|
data: T | null;
|
|
error: string | null;
|
|
lastLoadedAt: string | null;
|
|
}
|
|
|
|
export interface CommandSurfaceDoctorState
|
|
extends CommandSurfaceDiagnosticsPhaseState<DoctorReport> {
|
|
fixPending: boolean;
|
|
lastFixResult: DoctorFixResult | null;
|
|
lastFixError: string | null;
|
|
}
|
|
|
|
export interface CommandSurfaceDiagnosticsState {
|
|
forensics: CommandSurfaceDiagnosticsPhaseState<ForensicReport>;
|
|
doctor: CommandSurfaceDoctorState;
|
|
skillHealth: CommandSurfaceDiagnosticsPhaseState<SkillHealthReport>;
|
|
}
|
|
|
|
export function createInitialDiagnosticsPhaseState<
|
|
T,
|
|
>(): CommandSurfaceDiagnosticsPhaseState<T> {
|
|
return { phase: "idle", data: null, error: null, lastLoadedAt: null };
|
|
}
|
|
|
|
export function createInitialDoctorState(): CommandSurfaceDoctorState {
|
|
return {
|
|
phase: "idle",
|
|
data: null,
|
|
error: null,
|
|
lastLoadedAt: null,
|
|
fixPending: false,
|
|
lastFixResult: null,
|
|
lastFixError: null,
|
|
};
|
|
}
|
|
|
|
export function createInitialDiagnosticsState(): CommandSurfaceDiagnosticsState {
|
|
return {
|
|
forensics: createInitialDiagnosticsPhaseState<ForensicReport>(),
|
|
doctor: createInitialDoctorState(),
|
|
skillHealth: createInitialDiagnosticsPhaseState<SkillHealthReport>(),
|
|
};
|
|
}
|
|
|
|
// ─── Knowledge/Captures panel state ──────────────────────────────────────────
|
|
|
|
export interface CommandSurfaceKnowledgeCapturesResolveState {
|
|
pending: boolean;
|
|
lastError: string | null;
|
|
lastResult: CaptureResolveResult | null;
|
|
}
|
|
|
|
export interface CommandSurfaceKnowledgeCapturesState {
|
|
knowledge: CommandSurfaceDiagnosticsPhaseState<KnowledgeData>;
|
|
captures: CommandSurfaceDiagnosticsPhaseState<CapturesData>;
|
|
resolveRequest: CommandSurfaceKnowledgeCapturesResolveState;
|
|
}
|
|
|
|
export function createInitialKnowledgeCapturesState(): CommandSurfaceKnowledgeCapturesState {
|
|
return {
|
|
knowledge: createInitialDiagnosticsPhaseState<KnowledgeData>(),
|
|
captures: createInitialDiagnosticsPhaseState<CapturesData>(),
|
|
resolveRequest: { pending: false, lastError: null, lastResult: null },
|
|
};
|
|
}
|
|
|
|
// ─── Settings panel state ────────────────────────────────────────────────────
|
|
|
|
export type CommandSurfaceSettingsState =
|
|
CommandSurfaceDiagnosticsPhaseState<SettingsData>;
|
|
|
|
export function createInitialSettingsState(): CommandSurfaceSettingsState {
|
|
return createInitialDiagnosticsPhaseState<SettingsData>();
|
|
}
|
|
|
|
// ─── Remaining command surfaces state ────────────────────────────────────────
|
|
|
|
export interface CommandSurfaceRemainingState {
|
|
history: CommandSurfaceDiagnosticsPhaseState<HistoryData>;
|
|
inspect: CommandSurfaceDiagnosticsPhaseState<InspectData>;
|
|
hooks: CommandSurfaceDiagnosticsPhaseState<HooksData>;
|
|
exportData: CommandSurfaceDiagnosticsPhaseState<ExportResult>;
|
|
undo: CommandSurfaceDiagnosticsPhaseState<UndoInfo>;
|
|
cleanup: CommandSurfaceDiagnosticsPhaseState<CleanupData>;
|
|
steer: CommandSurfaceDiagnosticsPhaseState<SteerData>;
|
|
}
|
|
|
|
export function createInitialRemainingState(): CommandSurfaceRemainingState {
|
|
return {
|
|
history: createInitialDiagnosticsPhaseState<HistoryData>(),
|
|
inspect: createInitialDiagnosticsPhaseState<InspectData>(),
|
|
hooks: createInitialDiagnosticsPhaseState<HooksData>(),
|
|
exportData: createInitialDiagnosticsPhaseState<ExportResult>(),
|
|
undo: createInitialDiagnosticsPhaseState<UndoInfo>(),
|
|
cleanup: createInitialDiagnosticsPhaseState<CleanupData>(),
|
|
steer: createInitialDiagnosticsPhaseState<SteerData>(),
|
|
};
|
|
}
|
|
|
|
export interface WorkspaceCommandSurfaceState {
|
|
open: boolean;
|
|
activeSurface: BrowserSlashCommandSurface | null;
|
|
source: CommandSurfaceSource | null;
|
|
section: CommandSurfaceSection | null;
|
|
args: string;
|
|
pendingAction: CommandSurfacePendingAction | null;
|
|
selectedTarget: CommandSurfaceTarget | null;
|
|
lastError: string | null;
|
|
lastResult: string | null;
|
|
availableModels: CommandSurfaceModelOption[];
|
|
forkMessages: CommandSurfaceForkMessage[];
|
|
sessionStats: CommandSurfaceSessionStats | null;
|
|
lastCompaction: CommandSurfaceCompactionResult | null;
|
|
gitSummary: CommandSurfaceGitSummaryState;
|
|
recovery: CommandSurfaceRecoveryState;
|
|
diagnostics: CommandSurfaceDiagnosticsState;
|
|
knowledgeCaptures: CommandSurfaceKnowledgeCapturesState;
|
|
settingsData: CommandSurfaceSettingsState;
|
|
remainingCommands: CommandSurfaceRemainingState;
|
|
sessionBrowser: CommandSurfaceSessionBrowserState;
|
|
resumeRequest: CommandSurfaceSessionMutationState;
|
|
renameRequest: CommandSurfaceSessionMutationState;
|
|
settingsRequests: CommandSurfaceSettingsMutationState;
|
|
}
|
|
|
|
export interface CommandSurfaceOpenContext {
|
|
onboardingLocked?: boolean;
|
|
currentModel?: { provider?: string; modelId?: string } | null;
|
|
currentThinkingLevel?: string | null;
|
|
preferredProviderId?: string | null;
|
|
resumableSessions?: CommandSurfaceResumableSession[];
|
|
currentSessionPath?: string | null;
|
|
currentSessionName?: string | null;
|
|
projectCwd?: string | null;
|
|
projectSessionsDir?: string | null;
|
|
}
|
|
|
|
export interface CommandSurfaceOpenRequest extends CommandSurfaceOpenContext {
|
|
surface: BrowserSlashCommandSurface;
|
|
source: CommandSurfaceSource;
|
|
args?: string;
|
|
selectedTarget?: CommandSurfaceTarget | null;
|
|
}
|
|
|
|
export interface CommandSurfaceActionResult {
|
|
action: CommandSurfacePendingAction;
|
|
success: boolean;
|
|
message: string;
|
|
selectedTarget?: CommandSurfaceTarget | null;
|
|
availableModels?: CommandSurfaceModelOption[];
|
|
forkMessages?: CommandSurfaceForkMessage[];
|
|
sessionStats?: CommandSurfaceSessionStats | null;
|
|
lastCompaction?: CommandSurfaceCompactionResult | null;
|
|
gitSummary?: CommandSurfaceGitSummaryState;
|
|
recovery?: CommandSurfaceRecoveryState;
|
|
sessionBrowser?: CommandSurfaceSessionBrowserState;
|
|
}
|
|
|
|
const AUTH_SURFACE_COMMANDS = new Set<BrowserSlashCommandSurface>([
|
|
"settings",
|
|
"login",
|
|
"logout",
|
|
]);
|
|
const SETTINGS_MUTATION_ACTION_TO_REQUEST: Partial<
|
|
Record<CommandSurfacePendingAction, keyof CommandSurfaceSettingsMutationState>
|
|
> = {
|
|
set_steering_mode: "steeringMode",
|
|
set_follow_up_mode: "followUpMode",
|
|
set_auto_compaction: "autoCompaction",
|
|
set_auto_retry: "autoRetry",
|
|
abort_retry: "abortRetry",
|
|
};
|
|
|
|
function matchingSessionPath(
|
|
sessions: CommandSurfaceResumableSession[] | undefined,
|
|
query: string | undefined,
|
|
): string | undefined {
|
|
if (!sessions?.length) return undefined;
|
|
const normalizedQuery = query?.trim().toLowerCase();
|
|
if (!normalizedQuery) {
|
|
return (
|
|
sessions.find((session) => !session.isActive)?.path ?? sessions[0]?.path
|
|
);
|
|
}
|
|
|
|
const exactMatch = sessions.find((session) => {
|
|
const values = [session.id, session.name, session.path]
|
|
.filter(Boolean)
|
|
.map((value) => value!.toLowerCase());
|
|
return values.includes(normalizedQuery);
|
|
});
|
|
if (exactMatch) return exactMatch.path;
|
|
|
|
return sessions.find((session) => {
|
|
const values = [session.id, session.name, session.path]
|
|
.filter(Boolean)
|
|
.map((value) => value!.toLowerCase());
|
|
return values.some((value) => value.includes(normalizedQuery));
|
|
})?.path;
|
|
}
|
|
|
|
function createInitialCommandSurfaceSessionBrowserState(
|
|
overrides: Partial<CommandSurfaceSessionBrowserState> = {},
|
|
): CommandSurfaceSessionBrowserState {
|
|
return {
|
|
scope: null,
|
|
projectCwd: null,
|
|
projectSessionsDir: null,
|
|
activeSessionPath: null,
|
|
query: "",
|
|
sortMode: "threaded",
|
|
nameFilter: "all",
|
|
totalSessions: 0,
|
|
returnedSessions: 0,
|
|
sessions: [],
|
|
loaded: false,
|
|
error: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function createInitialCommandSurfaceSessionMutationState(): CommandSurfaceSessionMutationState {
|
|
return {
|
|
pending: false,
|
|
sessionPath: null,
|
|
result: null,
|
|
error: null,
|
|
};
|
|
}
|
|
|
|
function createInitialCommandSurfaceSettingMutationState(): CommandSurfaceSettingMutationState {
|
|
return {
|
|
pending: false,
|
|
result: null,
|
|
error: null,
|
|
};
|
|
}
|
|
|
|
function createInitialCommandSurfaceSettingsMutationState(): CommandSurfaceSettingsMutationState {
|
|
return {
|
|
steeringMode: createInitialCommandSurfaceSettingMutationState(),
|
|
followUpMode: createInitialCommandSurfaceSettingMutationState(),
|
|
autoCompaction: createInitialCommandSurfaceSettingMutationState(),
|
|
autoRetry: createInitialCommandSurfaceSettingMutationState(),
|
|
abortRetry: createInitialCommandSurfaceSettingMutationState(),
|
|
};
|
|
}
|
|
|
|
function createInitialCommandSurfaceGitSummaryState(): CommandSurfaceGitSummaryState {
|
|
return {
|
|
pending: false,
|
|
loaded: false,
|
|
result: null,
|
|
error: null,
|
|
};
|
|
}
|
|
|
|
export function createInitialCommandSurfaceRecoveryState(): CommandSurfaceRecoveryState {
|
|
return {
|
|
phase: "idle",
|
|
pending: false,
|
|
loaded: false,
|
|
stale: false,
|
|
diagnostics: null,
|
|
error: null,
|
|
lastLoadedAt: null,
|
|
lastInvalidatedAt: null,
|
|
lastFailureAt: null,
|
|
};
|
|
}
|
|
|
|
function buildInitialSessionBrowserState(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceSessionBrowserState {
|
|
const initialQuery =
|
|
request.surface === "resume" ? (request.args?.trim() ?? "") : "";
|
|
return createInitialCommandSurfaceSessionBrowserState({
|
|
activeSessionPath: request.currentSessionPath ?? null,
|
|
projectCwd: request.projectCwd ?? null,
|
|
projectSessionsDir: request.projectSessionsDir ?? null,
|
|
query: initialQuery,
|
|
sortMode: initialQuery ? "relevance" : "threaded",
|
|
});
|
|
}
|
|
|
|
export function isCommandSurfaceThinkingLevel(
|
|
value: string | null | undefined,
|
|
): value is CommandSurfaceThinkingLevel {
|
|
return COMMAND_SURFACE_THINKING_LEVELS.includes(
|
|
(value ?? "") as CommandSurfaceThinkingLevel,
|
|
);
|
|
}
|
|
|
|
export function createInitialCommandSurfaceState(): WorkspaceCommandSurfaceState {
|
|
return {
|
|
open: false,
|
|
activeSurface: null,
|
|
source: null,
|
|
section: null,
|
|
args: "",
|
|
pendingAction: null,
|
|
selectedTarget: null,
|
|
lastError: null,
|
|
lastResult: null,
|
|
availableModels: [],
|
|
forkMessages: [],
|
|
sessionStats: null,
|
|
lastCompaction: null,
|
|
gitSummary: createInitialCommandSurfaceGitSummaryState(),
|
|
recovery: createInitialCommandSurfaceRecoveryState(),
|
|
diagnostics: createInitialDiagnosticsState(),
|
|
knowledgeCaptures: createInitialKnowledgeCapturesState(),
|
|
settingsData: createInitialSettingsState(),
|
|
remainingCommands: createInitialRemainingState(),
|
|
sessionBrowser: createInitialCommandSurfaceSessionBrowserState(),
|
|
resumeRequest: createInitialCommandSurfaceSessionMutationState(),
|
|
renameRequest: createInitialCommandSurfaceSessionMutationState(),
|
|
settingsRequests: createInitialCommandSurfaceSettingsMutationState(),
|
|
};
|
|
}
|
|
|
|
export function commandSurfaceSectionForRequest(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceSection | null {
|
|
switch (request.surface) {
|
|
case "model":
|
|
return "model";
|
|
case "thinking":
|
|
return "thinking";
|
|
case "settings":
|
|
return request.onboardingLocked ? "auth" : "general";
|
|
case "git":
|
|
return "git";
|
|
case "login":
|
|
case "logout":
|
|
return "auth";
|
|
case "resume":
|
|
return "resume";
|
|
case "name":
|
|
return "name";
|
|
case "fork":
|
|
return "fork";
|
|
case "session":
|
|
case "export":
|
|
return "session";
|
|
case "compact":
|
|
return "compact";
|
|
// SF subcommand surfaces (S02)
|
|
case "sf-status":
|
|
return "sf-status";
|
|
case "sf-visualize":
|
|
return "sf-visualize";
|
|
case "sf-forensics":
|
|
return "sf-forensics";
|
|
case "sf-doctor":
|
|
return "sf-doctor";
|
|
case "sf-skill-health":
|
|
return "sf-skill-health";
|
|
case "sf-knowledge":
|
|
return "sf-knowledge";
|
|
case "sf-capture":
|
|
return "sf-capture";
|
|
case "sf-triage":
|
|
return "sf-triage";
|
|
case "sf-quick":
|
|
return "sf-quick";
|
|
case "sf-history":
|
|
return "sf-history";
|
|
case "sf-undo":
|
|
return "sf-undo";
|
|
case "sf-inspect":
|
|
return "sf-inspect";
|
|
case "sf-prefs":
|
|
return "sf-prefs";
|
|
case "sf-config":
|
|
return "sf-config";
|
|
case "sf-hooks":
|
|
return "sf-hooks";
|
|
case "sf-mode":
|
|
return "sf-mode";
|
|
case "sf-steer":
|
|
return "sf-steer";
|
|
case "sf-export":
|
|
return "sf-export";
|
|
case "sf-cleanup":
|
|
return "sf-cleanup";
|
|
case "sf-queue":
|
|
return "sf-queue";
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function buildSettingsTarget(
|
|
section: CommandSurfaceSection,
|
|
): CommandSurfaceTarget {
|
|
return { kind: "settings", section };
|
|
}
|
|
|
|
function buildModelTarget(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceTarget {
|
|
const query = request.args?.trim() || undefined;
|
|
return {
|
|
kind: "model",
|
|
provider: request.currentModel?.provider,
|
|
modelId: request.currentModel?.modelId,
|
|
query,
|
|
};
|
|
}
|
|
|
|
function buildThinkingTarget(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceTarget {
|
|
const requestedLevel = request.args?.trim().toLowerCase() || "";
|
|
const level = isCommandSurfaceThinkingLevel(requestedLevel)
|
|
? requestedLevel
|
|
: isCommandSurfaceThinkingLevel(request.currentThinkingLevel)
|
|
? request.currentThinkingLevel
|
|
: "off";
|
|
|
|
return {
|
|
kind: "thinking",
|
|
level,
|
|
};
|
|
}
|
|
|
|
function buildAuthTarget(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceTarget {
|
|
const requestedProviderId = request.args?.trim() || undefined;
|
|
return {
|
|
kind: "auth",
|
|
providerId: requestedProviderId ?? request.preferredProviderId ?? undefined,
|
|
intent:
|
|
request.surface === "login"
|
|
? "login"
|
|
: request.surface === "logout"
|
|
? "logout"
|
|
: "manage",
|
|
};
|
|
}
|
|
|
|
function buildResumeTarget(
|
|
request: CommandSurfaceOpenRequest,
|
|
): Extract<CommandSurfaceTarget, { kind: "resume" }> {
|
|
const selectedPath = matchingSessionPath(
|
|
request.resumableSessions,
|
|
request.args,
|
|
);
|
|
return {
|
|
kind: "resume",
|
|
sessionPath: selectedPath,
|
|
};
|
|
}
|
|
|
|
function buildNameTarget(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceTarget {
|
|
const providedName = request.args?.trim();
|
|
return {
|
|
kind: "name",
|
|
sessionPath: request.currentSessionPath ?? undefined,
|
|
name:
|
|
providedName !== undefined && providedName.length > 0
|
|
? providedName
|
|
: (request.currentSessionName?.trim() ?? ""),
|
|
};
|
|
}
|
|
|
|
function buildForkTarget(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceTarget {
|
|
const entryId = request.args?.trim() || undefined;
|
|
return {
|
|
kind: "fork",
|
|
entryId,
|
|
};
|
|
}
|
|
|
|
function buildSessionTarget(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceTarget {
|
|
const outputPath = request.args?.trim() || undefined;
|
|
return {
|
|
kind: "session",
|
|
outputPath,
|
|
};
|
|
}
|
|
|
|
function buildCompactTarget(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceTarget {
|
|
return {
|
|
kind: "compact",
|
|
customInstructions: request.args?.trim() ?? "",
|
|
};
|
|
}
|
|
|
|
export function buildCommandSurfaceTarget(
|
|
request: CommandSurfaceOpenRequest,
|
|
): CommandSurfaceTarget | null {
|
|
if (request.selectedTarget !== undefined) {
|
|
return request.selectedTarget;
|
|
}
|
|
|
|
const section = commandSurfaceSectionForRequest(request);
|
|
if (!section) return null;
|
|
|
|
if (request.surface === "settings") {
|
|
return buildSettingsTarget(section);
|
|
}
|
|
|
|
if (request.surface === "model") {
|
|
return buildModelTarget(request);
|
|
}
|
|
|
|
if (request.surface === "thinking") {
|
|
return buildThinkingTarget(request);
|
|
}
|
|
|
|
if (AUTH_SURFACE_COMMANDS.has(request.surface)) {
|
|
return buildAuthTarget(request);
|
|
}
|
|
|
|
if (request.surface === "resume") {
|
|
return buildResumeTarget(request);
|
|
}
|
|
|
|
if (request.surface === "name") {
|
|
return buildNameTarget(request);
|
|
}
|
|
|
|
if (request.surface === "fork") {
|
|
return buildForkTarget(request);
|
|
}
|
|
|
|
if (request.surface === "session" || request.surface === "export") {
|
|
return buildSessionTarget(request);
|
|
}
|
|
|
|
if (request.surface === "compact") {
|
|
return buildCompactTarget(request);
|
|
}
|
|
|
|
// SF subcommand surfaces — generic target (S02)
|
|
if (request.surface?.startsWith("sf-")) {
|
|
const subcommand = request.surface.slice(3); // "sf-forensics" -> "forensics"
|
|
return {
|
|
kind: "sf",
|
|
surface: request.surface,
|
|
subcommand,
|
|
args: request.args ?? "",
|
|
};
|
|
}
|
|
|
|
return buildSettingsTarget(section);
|
|
}
|
|
|
|
export function openCommandSurfaceState(
|
|
current: WorkspaceCommandSurfaceState,
|
|
request: CommandSurfaceOpenRequest,
|
|
): WorkspaceCommandSurfaceState {
|
|
const section = commandSurfaceSectionForRequest(request);
|
|
return {
|
|
...current,
|
|
open: true,
|
|
activeSurface: request.surface,
|
|
source: request.source,
|
|
section,
|
|
args: request.args?.trim() ?? "",
|
|
pendingAction: null,
|
|
selectedTarget: buildCommandSurfaceTarget(request),
|
|
lastError: null,
|
|
lastResult: null,
|
|
sessionStats: null,
|
|
forkMessages: [],
|
|
lastCompaction: null,
|
|
gitSummary: createInitialCommandSurfaceGitSummaryState(),
|
|
recovery: createInitialCommandSurfaceRecoveryState(),
|
|
diagnostics: createInitialDiagnosticsState(),
|
|
knowledgeCaptures: createInitialKnowledgeCapturesState(),
|
|
settingsData: createInitialSettingsState(),
|
|
remainingCommands: createInitialRemainingState(),
|
|
sessionBrowser: buildInitialSessionBrowserState(request),
|
|
resumeRequest: createInitialCommandSurfaceSessionMutationState(),
|
|
renameRequest: createInitialCommandSurfaceSessionMutationState(),
|
|
settingsRequests: createInitialCommandSurfaceSettingsMutationState(),
|
|
};
|
|
}
|
|
|
|
export function closeCommandSurfaceState(
|
|
current: WorkspaceCommandSurfaceState,
|
|
): WorkspaceCommandSurfaceState {
|
|
return {
|
|
...current,
|
|
open: false,
|
|
pendingAction: null,
|
|
};
|
|
}
|
|
|
|
export function setCommandSurfaceSection(
|
|
current: WorkspaceCommandSurfaceState,
|
|
section: CommandSurfaceSection,
|
|
context: CommandSurfaceOpenContext = {},
|
|
): WorkspaceCommandSurfaceState {
|
|
const request: CommandSurfaceOpenRequest = {
|
|
surface: current.activeSurface ?? "settings",
|
|
source: current.source ?? "surface",
|
|
args: current.args,
|
|
...context,
|
|
};
|
|
|
|
const currentSessionPath =
|
|
current.selectedTarget?.kind === "resume"
|
|
? current.selectedTarget.sessionPath
|
|
: current.selectedTarget?.kind === "name"
|
|
? current.selectedTarget.sessionPath
|
|
: undefined;
|
|
const currentDraftName =
|
|
current.selectedTarget?.kind === "name"
|
|
? current.selectedTarget.name
|
|
: undefined;
|
|
|
|
let selectedTarget: CommandSurfaceTarget | null = current.selectedTarget;
|
|
if (section === "model") {
|
|
selectedTarget = buildModelTarget(request);
|
|
} else if (section === "thinking") {
|
|
selectedTarget = buildThinkingTarget(request);
|
|
} else if (
|
|
section === "general" ||
|
|
section === "session-behavior" ||
|
|
section === "queue" ||
|
|
section === "compaction" ||
|
|
section === "retry" ||
|
|
section === "recovery" ||
|
|
section === "git" ||
|
|
section === "admin"
|
|
) {
|
|
selectedTarget = buildSettingsTarget(section);
|
|
} else if (section === "auth") {
|
|
selectedTarget = buildAuthTarget({
|
|
...request,
|
|
surface:
|
|
current.activeSurface === "logout"
|
|
? "logout"
|
|
: current.activeSurface === "login"
|
|
? "login"
|
|
: "settings",
|
|
});
|
|
} else if (section === "resume") {
|
|
selectedTarget = {
|
|
kind: "resume",
|
|
sessionPath: currentSessionPath ?? buildResumeTarget(request).sessionPath,
|
|
};
|
|
} else if (section === "name") {
|
|
selectedTarget = {
|
|
kind: "name",
|
|
sessionPath:
|
|
currentSessionPath ?? request.currentSessionPath ?? undefined,
|
|
name: currentDraftName ?? request.currentSessionName?.trim() ?? "",
|
|
};
|
|
} else if (section === "fork") {
|
|
selectedTarget = buildForkTarget(request);
|
|
} else if (section === "session") {
|
|
selectedTarget = buildSessionTarget(request);
|
|
} else if (section === "compact") {
|
|
selectedTarget = buildCompactTarget(request);
|
|
}
|
|
|
|
return {
|
|
...current,
|
|
section,
|
|
selectedTarget,
|
|
};
|
|
}
|
|
|
|
export function selectCommandSurfaceStateTarget(
|
|
current: WorkspaceCommandSurfaceState,
|
|
target: CommandSurfaceTarget,
|
|
): WorkspaceCommandSurfaceState {
|
|
const nextSection =
|
|
target.kind === "settings"
|
|
? target.section
|
|
: target.kind === "model"
|
|
? "model"
|
|
: target.kind === "thinking"
|
|
? "thinking"
|
|
: target.kind === "auth"
|
|
? "auth"
|
|
: target.kind === "resume"
|
|
? "resume"
|
|
: target.kind === "name"
|
|
? "name"
|
|
: target.kind === "fork"
|
|
? "fork"
|
|
: target.kind === "session"
|
|
? "session"
|
|
: "compact";
|
|
|
|
return {
|
|
...current,
|
|
section: nextSection,
|
|
selectedTarget: target,
|
|
lastError: null,
|
|
lastResult: null,
|
|
};
|
|
}
|
|
|
|
export function setCommandSurfacePending(
|
|
current: WorkspaceCommandSurfaceState,
|
|
action: CommandSurfacePendingAction,
|
|
selectedTarget: CommandSurfaceTarget | null = current.selectedTarget,
|
|
): WorkspaceCommandSurfaceState {
|
|
const nextResumeRequest =
|
|
action === "switch_session"
|
|
? {
|
|
pending: true,
|
|
sessionPath:
|
|
selectedTarget?.kind === "resume"
|
|
? (selectedTarget.sessionPath ?? null)
|
|
: null,
|
|
result: null,
|
|
error: null,
|
|
}
|
|
: current.resumeRequest;
|
|
|
|
const nextRenameRequest =
|
|
action === "rename_session"
|
|
? {
|
|
pending: true,
|
|
sessionPath:
|
|
selectedTarget?.kind === "name"
|
|
? (selectedTarget.sessionPath ?? null)
|
|
: null,
|
|
result: null,
|
|
error: null,
|
|
}
|
|
: current.renameRequest;
|
|
|
|
const settingsRequestKey = SETTINGS_MUTATION_ACTION_TO_REQUEST[action];
|
|
const nextSettingsRequests = settingsRequestKey
|
|
? {
|
|
...current.settingsRequests,
|
|
[settingsRequestKey]: {
|
|
pending: true,
|
|
result: null,
|
|
error: null,
|
|
},
|
|
}
|
|
: current.settingsRequests;
|
|
|
|
return {
|
|
...current,
|
|
pendingAction: action,
|
|
selectedTarget,
|
|
lastError: null,
|
|
lastResult: null,
|
|
gitSummary:
|
|
action === "load_git_summary"
|
|
? {
|
|
...current.gitSummary,
|
|
pending: true,
|
|
error: null,
|
|
}
|
|
: current.gitSummary,
|
|
recovery:
|
|
action === "load_recovery_diagnostics"
|
|
? {
|
|
...current.recovery,
|
|
pending: true,
|
|
error: null,
|
|
phase: current.recovery.loaded ? current.recovery.phase : "loading",
|
|
}
|
|
: current.recovery,
|
|
sessionBrowser:
|
|
action === "load_session_browser"
|
|
? {
|
|
...current.sessionBrowser,
|
|
error: null,
|
|
}
|
|
: current.sessionBrowser,
|
|
resumeRequest: nextResumeRequest,
|
|
renameRequest: nextRenameRequest,
|
|
settingsRequests: nextSettingsRequests,
|
|
};
|
|
}
|
|
|
|
export function applyCommandSurfaceActionResult(
|
|
current: WorkspaceCommandSurfaceState,
|
|
result: CommandSurfaceActionResult,
|
|
): WorkspaceCommandSurfaceState {
|
|
const nextSelectedTarget =
|
|
result.selectedTarget === undefined
|
|
? current.selectedTarget
|
|
: result.selectedTarget;
|
|
const resumeSessionPath =
|
|
(nextSelectedTarget?.kind === "resume"
|
|
? nextSelectedTarget.sessionPath
|
|
: undefined) ?? current.resumeRequest.sessionPath;
|
|
const renameSessionPath =
|
|
(nextSelectedTarget?.kind === "name"
|
|
? nextSelectedTarget.sessionPath
|
|
: undefined) ?? current.renameRequest.sessionPath;
|
|
const settingsRequestKey = SETTINGS_MUTATION_ACTION_TO_REQUEST[result.action];
|
|
const nextSettingsRequests = settingsRequestKey
|
|
? {
|
|
...current.settingsRequests,
|
|
[settingsRequestKey]: {
|
|
pending: false,
|
|
result: result.success ? result.message : null,
|
|
error: result.success ? null : result.message,
|
|
},
|
|
}
|
|
: current.settingsRequests;
|
|
|
|
return {
|
|
...current,
|
|
pendingAction: null,
|
|
selectedTarget: nextSelectedTarget,
|
|
availableModels: result.availableModels ?? current.availableModels,
|
|
forkMessages: result.forkMessages ?? current.forkMessages,
|
|
sessionStats:
|
|
result.sessionStats === undefined
|
|
? current.sessionStats
|
|
: result.sessionStats,
|
|
lastCompaction:
|
|
result.lastCompaction === undefined
|
|
? current.lastCompaction
|
|
: result.lastCompaction,
|
|
gitSummary:
|
|
result.gitSummary === undefined
|
|
? current.gitSummary
|
|
: {
|
|
...result.gitSummary,
|
|
pending: false,
|
|
loaded: result.gitSummary.loaded || result.success,
|
|
},
|
|
recovery: result.recovery ?? current.recovery,
|
|
sessionBrowser: result.sessionBrowser ?? current.sessionBrowser,
|
|
resumeRequest:
|
|
result.action === "switch_session"
|
|
? {
|
|
pending: false,
|
|
sessionPath: resumeSessionPath ?? null,
|
|
result: result.success ? result.message : null,
|
|
error: result.success ? null : result.message,
|
|
}
|
|
: current.resumeRequest,
|
|
renameRequest:
|
|
result.action === "rename_session"
|
|
? {
|
|
pending: false,
|
|
sessionPath: renameSessionPath ?? null,
|
|
result: result.success ? result.message : null,
|
|
error: result.success ? null : result.message,
|
|
}
|
|
: current.renameRequest,
|
|
settingsRequests: nextSettingsRequests,
|
|
lastError: result.success ? null : result.message,
|
|
lastResult: result.success ? result.message : null,
|
|
};
|
|
}
|
|
|
|
export function surfaceOutcomeToOpenRequest(
|
|
outcome: Extract<BrowserSlashCommandDispatchResult, { kind: "surface" }>,
|
|
context: CommandSurfaceOpenContext = {},
|
|
): CommandSurfaceOpenRequest {
|
|
return {
|
|
surface: outcome.surface,
|
|
source: "slash",
|
|
args: outcome.args,
|
|
...context,
|
|
};
|
|
}
|