Skip to main content
Glama
command-guard.ts4.66 kB
/** * @fileoverview Command guard for CLI commands * CLI presentation layer - uses tm-core for logic, displays with cardBox */ import { type LocalOnlyCommand, type TmCore, createTmCore } from '@tm/core'; import chalk from 'chalk'; import { displayCardBox } from '../ui/components/cardBox.component.js'; /** * Command-specific messaging configuration */ interface CommandMessage { header: string; getBody: (briefName: string) => string[]; footer: string; } /** * Get command-specific message configuration * * NOTE: Command groups below are intentionally hardcoded (not imported from LOCAL_ONLY_COMMANDS) * to allow flexible categorization with custom messaging per category. All commands here are * subsets of LOCAL_ONLY_COMMANDS from @tm/core, which is the source of truth for blocked commands. * * Categories exist for UX purposes (tailored messaging), while LOCAL_ONLY_COMMANDS exists for * enforcement (what's actually blocked when using Hamster). */ function getCommandMessage(commandName: LocalOnlyCommand): CommandMessage { // Dependency management commands if ( [ 'add-dependency', 'remove-dependency', 'validate-dependencies', 'fix-dependencies' ].includes(commandName) ) { return { header: 'Hamster Manages Dependencies', getBody: (briefName) => [ `Hamster handles dependencies for the ${chalk.blue(`"${briefName}"`)} Brief.`, `To manage dependencies manually, log out with ${chalk.cyan('tm auth logout')} and work locally.` ], footer: 'Switch between local and remote workflows anytime by logging in/out.' }; } // Subtask management commands if (commandName === 'clear-subtasks') { return { header: 'Hamster Manages Subtasks', getBody: (briefName) => [ `Hamster handles subtask management for the ${chalk.blue(`"${briefName}"`)} Brief.`, `To manage subtasks manually, log out with ${chalk.cyan('tm auth logout')} and work locally.` ], footer: 'Switch between local and remote workflows anytime by logging in/out.' }; } // Model configuration commands if (commandName === 'models') { return { header: 'Hamster Manages AI Models', getBody: (briefName) => [ `Hamster configures AI models automatically for the ${chalk.blue(`"${briefName}"`)} Brief.`, `To configure models manually, log out with ${chalk.cyan('tm auth logout')} and work locally.` ], footer: 'Switch between local and remote workflows anytime by logging in/out.' }; } // Default message for any other local-only commands return { header: 'Command Not Available in Hamster', getBody: (briefName) => [ `The ${chalk.cyan(commandName)} command is managed by Hamster for the ${chalk.blue(`"${briefName}"`)} Brief.`, `To use this command, log out with ${chalk.cyan('tm auth logout')} and work locally.` ], footer: 'Switch between local and remote workflows anytime by logging in/out.' }; } /** * Check if a command should be blocked when authenticated and display CLI message * * Use this for CLI commands that are only available for local file storage (not Hamster). * This uses tm-core's AuthDomain.guardCommand() and formats the result for CLI display. * * @param commandName - Name of the command being executed * @param tmCoreOrProjectRoot - TmCore instance or project root path * @returns true if command should be blocked, false otherwise * * @example * ```ts * const isBlocked = await checkAndBlockIfAuthenticated('add-dependency', projectRoot); * if (isBlocked) { * process.exit(1); * } * ``` */ export async function checkAndBlockIfAuthenticated( commandName: string, tmCoreOrProjectRoot: TmCore | string ): Promise<boolean> { // Get or create TmCore instance const tmCore = typeof tmCoreOrProjectRoot === 'string' ? await createTmCore({ projectPath: tmCoreOrProjectRoot }) : tmCoreOrProjectRoot; // Use tm-core's auth domain to check the command guard const result = await tmCore.auth.guardCommand( commandName, tmCore.tasks.getStorageType() ); if (result.isBlocked) { // Get command-specific message configuration // Safe to cast: guardCommand only blocks commands in LOCAL_ONLY_COMMANDS const message = getCommandMessage(commandName as LocalOnlyCommand); const briefName = result.briefName || 'remote brief'; // Format and display CLI message with cardBox console.log( displayCardBox({ header: message.header, body: message.getBody(briefName), footer: message.footer, level: 'info' }) ); return true; } return false; } // Legacy export for backward compatibility export const checkAndBlockDependencyCommand = checkAndBlockIfAuthenticated;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/eyaltoledano/claude-task-master'

If you have feedback or need assistance with the MCP directory API, please join our Discord server