/** * Copyright (c) 2026, Salesforce, Inc., * All rights reserved. * For full license text, see the LICENSE.txt file */ import type { GraphQLSchema } from "graphql"; import type { QuerySession } from "./session.js"; export type OutputMode = "human" | "json" | "quiet"; export interface CommandOpts { search?: string; regex?: string; long?: boolean; all?: boolean; as?: string; quiet?: boolean; dataCloud?: boolean; dryRun?: boolean; strict?: boolean; force?: boolean; mutation?: boolean; name?: string; out?: string; var?: string[]; json?: boolean; olderThan?: string; } export interface CommandContext { session: QuerySession; schema: GraphQLSchema; args: string[]; opts: CommandOpts; output: OutputMode; } export type CommandResult = Record; export interface CommandSubcommand { name: string; usage: string; description: string; } export interface CommandDef { name: string; aliases: string[]; summary: string; usage: string; examples: string[]; subcommands?: CommandSubcommand[]; mutates: boolean; requiresSession: boolean; category: "navigation" | "building" | "args" | "review" | "session" | "meta"; } export const COMMANDS: CommandDef[] = [ // Navigation { name: "pwd", aliases: [], summary: "Print current navigation path", usage: "pwd", examples: [], mutates: false, requiresSession: true, category: "navigation", }, { name: "cd", aliases: [], summary: "Navigate the schema tree", usage: "cd ", examples: ["cd uiapi/query/Account", "cd edges/node", "cd @args/where", "cd ..", "cd /"], mutates: true, requiresSession: true, category: "navigation", }, { name: "ls", aliases: [], summary: "List fields at current or specified path", usage: "ls [-l] [-a] [--search ] [ ...]", examples: ["ls", "ls -l", "ls -a", "ls --search Name", "ls Name Owner Status"], mutates: false, requiresSession: true, category: "navigation", }, // Building { name: "select", aliases: [], summary: "Select leaf fields for the query projection", usage: "select [...] | select ls", examples: [ "select Id Name.value:name", "select uiapi/query/Case/edges/node/Id", "select Owner.on:User.Name.value:ownerName", "select ls", ], subcommands: [ { name: "ls", usage: "select ls", description: "List all currently selected fields" }, ], mutates: true, requiresSession: true, category: "building", }, { name: "drop", aliases: [], summary: "Remove a field, alias, variable, or arg value", usage: "drop ", examples: ["drop ownerName", "drop Owner.Name.value", "drop $filter"], mutates: true, requiresSession: true, category: "building", }, { name: "alias", aliases: [], summary: "Create an aliased field instance or add a list element", usage: "alias ", examples: ["alias openCases Case", "alias myCases Case"], mutates: true, requiresSession: true, category: "building", }, { name: "undo", aliases: [], summary: "Revert the last state-mutating operation", usage: "undo", examples: [], mutates: false, requiresSession: true, category: "building", }, { name: "optional", aliases: [], summary: "Toggle @optional directive on selected fields (FLS safety)", usage: "optional [...] | optional --remove [...]", examples: ["optional Status Subject", "optional --remove Status"], mutates: true, requiresSession: true, category: "building", }, // Args & Variables { name: "set", aliases: [], summary: "Set field arguments or variable values", usage: "set [] = [...] | set ", examples: [ "set first=10", "set uiapi/query/Case first=10 scope=MINE", 'set where \'{"Status":{"eq":"New"}}\'', "set @args/first 10", ], mutates: true, requiresSession: true, category: "args", }, { name: "unset", aliases: [], summary: "Remove an assigned argument value", usage: "unset ", examples: ["unset first", "unset where/Name/like"], mutates: true, requiresSession: true, category: "args", }, { name: "var", aliases: [], summary: "Define a query variable (type auto-inferred from path)", usage: "var $name [default]", examples: [ "var $filter @args/where", "var $limit @args/first 10", "var $caseId uiapi/query/Case/@args/where/Id/eq", ], mutates: true, requiresSession: true, category: "args", }, // Review & Execute { name: "show", aliases: [], summary: "Full session snapshot: path, query, variables, args", usage: "show", examples: [], mutates: false, requiresSession: true, category: "review", }, { name: "check", aliases: [], summary: "Validate the query against the schema", usage: "check [--strict]", examples: [], mutates: false, requiresSession: true, category: "review", }, { name: "run", aliases: [], summary: "Execute the query against the org", usage: "run [--dry-run] [--var key=value ...]", examples: ["run", "run --dry-run", "run --var caseId=500xxx"], mutates: false, requiresSession: true, category: "review", }, { name: "describe", aliases: [], summary: "Inspect an SObject with enriched metadata", usage: "describe []", examples: ["describe Case", "describe Account", "describe"], mutates: false, requiresSession: false, category: "review", }, { name: "codegen", aliases: [], summary: "Generate client types for the query (default: TypeScript)", usage: "codegen [--language ] [--out ] [--name ]", examples: [ "codegen", "codegen --language typescript", "codegen -l ts --out types.ts", "codegen --name CaseDetail", ], mutates: false, requiresSession: true, category: "review", }, // Session management { name: "new", aliases: [], summary: "Create a new query session", usage: "new [--mutation] [--aggregate] [--name ] [--force]", examples: [ "new myorg", "new myorg --mutation --name createCase", "new myorg --aggregate --name orderCounts", ], mutates: false, requiresSession: false, category: "session", }, { name: "use", aliases: [], summary: "Set the active session", usage: "use ", examples: ["use s_abc123", "use myQuery"], mutates: false, requiresSession: false, category: "session", }, { name: "sessions", aliases: [], summary: "List, delete, prune, or clean sessions", usage: "sessions [rm | prune --older-than | clean]", examples: [ "sessions", "sessions rm myQuery", "sessions prune --older-than 7d", "sessions clean", ], subcommands: [ { name: "ls", usage: "sessions", description: "List all saved sessions (default)" }, { name: "rm", usage: "sessions rm | sessions rm --all", description: "Delete a session (or all sessions)", }, { name: "prune", usage: "sessions prune --older-than ", description: "Delete old sessions", }, { name: "clean", usage: "sessions clean", description: "Remove all sessions that fail validation", }, ], mutates: false, requiresSession: false, category: "session", }, { name: "clone", aliases: [], summary: "Duplicate session into a new one", usage: "clone [name] [--force] [--set path key=val] [--var $name path] [--unset path]", examples: [ "clone myClone", "clone variant --force --set uiapi/query/Case where=...", "clone mine --force --unset where/Status -s base", ], mutates: false, requiresSession: true, category: "session", }, { name: "reset", aliases: [], summary: "Reset session to empty state", usage: "reset", examples: [], mutates: true, requiresSession: true, category: "session", }, // Meta / Other { name: "chain", aliases: [], summary: "Run multiple commands in one invocation, separated by semicolons", usage: 'graphiti chain -s "cmd1; cmd2; cmd3" [--json]', examples: [ 'graphiti chain -s cases "cd uiapi/query/Case; set first=10; select edges/node/Id; check"', ], mutates: false, requiresSession: true, category: "meta", }, { name: "connect", aliases: [], summary: "Download and cache a schema for an org", usage: "connect [--refresh]", examples: ["connect myorg", "connect myorg --refresh"], mutates: false, requiresSession: false, category: "meta", }, { name: "orgs", aliases: [], summary: "List available Salesforce orgs", usage: "orgs", examples: [], mutates: false, requiresSession: false, category: "meta", }, { name: "help", aliases: [], summary: "Show help for a command or topic", usage: "help []", examples: ["help", "help select", "help set"], mutates: false, requiresSession: false, category: "meta", }, { name: "interactive", aliases: [], summary: "Start an interactive REPL session", usage: "interactive", examples: [], mutates: false, requiresSession: true, category: "meta", }, ]; export const MUTATING_COMMANDS = new Set(COMMANDS.filter((c) => c.mutates).map((c) => c.name)); const commandsByName = new Map(); const commandsByAlias = new Map(); for (const cmd of COMMANDS) { commandsByName.set(cmd.name, cmd); for (const alias of cmd.aliases) { commandsByAlias.set(alias, cmd); } } export function resolveCommand(name: string): CommandDef | undefined { return commandsByName.get(name) ?? commandsByAlias.get(name); } export function getCommandNames(): string[] { return COMMANDS.map((c) => c.name); } export function getAllCommandNamesAndAliases(): string[] { const names: string[] = []; for (const cmd of COMMANDS) { names.push(cmd.name); names.push(...cmd.aliases); } return names; } let _outputMode: OutputMode = "human"; export function setOutputMode(mode: OutputMode): void { _outputMode = mode; } export function getOutputMode(): OutputMode { return _outputMode; } let _interactiveMode = false; export function setInteractiveMode(on: boolean): void { _interactiveMode = on; } export function isInteractiveMode(): boolean { return _interactiveMode; }