From 70a2bfbac9fd368dae5f4e9773360b7484eaf021 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Sun, 21 Sep 2025 13:28:20 -0700 Subject: [PATCH 1/5] store ui: tweak virtalization params --- web/src/app/store/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/app/store/page.tsx b/web/src/app/store/page.tsx index f6512bff2f..a55a02f698 100644 --- a/web/src/app/store/page.tsx +++ b/web/src/app/store/page.tsx @@ -185,8 +185,8 @@ const AgentStorePage = () => { // Virtualizer for All Agents section only const allAgentsVirtualizer = useWindowVirtualizer({ count: isMounted ? allAgentsRows.length : 0, - estimateSize: () => 280, // Height for agent rows (card + gap) - overscan: 4, + estimateSize: () => 270, // Height for agent rows (card + gap) + overscan: 6, useAnimationFrameWithResizeObserver: true, }) From eb11ffaaecd72b78edd5b37ecf09c6af5e303855 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Sun, 21 Sep 2025 15:01:21 -0700 Subject: [PATCH 2/5] add cli shims feature --- knowledge.md | 54 +- npm-app/src/cli-definitions.ts | 5 + npm-app/src/cli-handlers/shims.ts | 145 +++++ npm-app/src/index.ts | 60 +- npm-app/src/shell-dispatcher.ts | 939 ++++++++++++++++++++++++++++++ 5 files changed, 1200 insertions(+), 3 deletions(-) create mode 100644 npm-app/src/cli-handlers/shims.ts create mode 100644 npm-app/src/shell-dispatcher.ts diff --git a/knowledge.md b/knowledge.md index cffa1fe270..5f6565a1c8 100644 --- a/knowledge.md +++ b/knowledge.md @@ -53,7 +53,57 @@ Codebuff is a tool for editing codebases via natural language instruction to Buf ## CLI Interface Features - ESC key to toggle menu or stop AI response -- CTRL+C to exit the application +- CTRL+C to exit the application## Shell Shims (Direct Commands) + +Codebuff supports shell shims for direct command invocation without the `codebuff` prefix. + +### Features + +- **Cross-platform**: Works on Windows (CMD/PowerShell), macOS, and Linux (bash/zsh/fish) +- **Store integration**: Uses fully qualified agent IDs from the agent store +- **Auto-naming**: Automatically uses the agent-id part as the command name +- **Custom names**: Optional custom command names via colon syntax +- **Safe installation**: Creates executable files in `~/.config/manicode/bin` (Unix) or `%USERPROFILE%/AppData/Local/Manicode/bin` (Windows) +- **PATH integration**: Provides instructions to add shims directory to shell PATH +- **Conflict detection**: Warns about existing commands with same names +- **Easy management**: Install, update, list, and uninstall shims via CLI commands### Quick Start (Recommended) + +```bash +# One-step setup: install and add to PATH automatically +codebuff shims install codebuff/base-lite@1.0.0 + +# Use immediately in current session (follow the printed instruction) +eval "$(codebuff shims env)" + +# Now use direct commands! +base-lite "fix this bug" # Works right away! +``` + +### Management Commands + +```bash +codebuff shims list # List installed shims +codebuff shims upgrade # Upgrade all shims to latest versions +codebuff shims doctor # Check shim health and PATH +codebuff shims env # Get eval command for current session +codebuff shims uninstall # Remove all shims +``` + +### Agent ID Format + +Agent IDs must be fully qualified store IDs: + +- Format: `publisher/agent-id@version` +- Example: `codebuff/base-lite@1.0.0` +- Custom command: `codebuff/base-lite@1.0.0:fast` + +### Benefits of New Setup + +- **One command setup**: Install automatically adds to PATH +- **Shell detection**: Automatically detects bash/zsh/fish/PowerShell +- **Safe editing**: Creates backups and uses idempotent markers +- **Immediate use**: `eval` commands work without restarting terminal +- **Cross-platform**: Works on macOS, Linux, and Windows ## Package Management @@ -326,11 +376,13 @@ Templates are maintained in the codebuff community repo. Each directory correspo **Important**: When adding database indexes or schema changes, modify the schema file directly (`common/src/db/schema.ts`) using Drizzle's index syntax, then run the migration generation script to create the actual migration files. **Do NOT** write migration SQL files directly. The proper workflow is: + 1. Update `common/src/db/schema.ts` with new indexes using Drizzle syntax 2. Run the migration generation script to create the SQL migration files 3. Apply the migrations using the deployment process Example of adding performance indexes: + ```typescript index('idx_table_optimized') .on(table.column1, table.column2) diff --git a/npm-app/src/cli-definitions.ts b/npm-app/src/cli-definitions.ts index 25ac71653a..533932dc11 100644 --- a/npm-app/src/cli-definitions.ts +++ b/npm-app/src/cli-definitions.ts @@ -93,4 +93,9 @@ export const cliOptions: CliParam[] = [ menuDescription: 'Log subagent messages to trace files', hidden: false, }, + { + flags: '--force', + description: 'Force overwrite existing shims', + hidden: true, + }, ] diff --git a/npm-app/src/cli-handlers/shims.ts b/npm-app/src/cli-handlers/shims.ts new file mode 100644 index 0000000000..0ebf18ede2 --- /dev/null +++ b/npm-app/src/cli-handlers/shims.ts @@ -0,0 +1,145 @@ +import { red, green, yellow } from 'picocolors' + +import { + installShims, + uninstallShims, + listShims, + updateShims, + doctorShims, + upgradeShims, +} from '../shell-dispatcher' +import { logger } from '../utils/logger' + +/** + * Handle the 'shims install' command + */ +export async function handleShimsInstall( + agentSpecs: string[], + options: { force?: boolean } = {}, +): Promise { + try { + if (!agentSpecs || agentSpecs.length === 0) { + console.error(red('Error: No agent IDs specified to install as shims.')) + console.log( + 'Usage: codebuff shims install [publisher/agent-id@version:custom-command] ...', + ) + process.exit(1) + } + installShims(agentSpecs, options) + } catch (error) { + logger.error( + { + errorMessage: error instanceof Error ? error.message : String(error), + errorStack: error instanceof Error ? error.stack : undefined, + agentSpecs, + options, + }, + 'Error installing shims', + ) + console.error(red(`Error installing shims: ${error}`)) + process.exit(1) + } +} + +/** + * Handle the 'shims uninstall' command + */ +export async function handleShimsUninstall( + commandNames?: string[], +): Promise { + try { + uninstallShims(commandNames) + } catch (error) { + logger.error( + { + errorMessage: error instanceof Error ? error.message : String(error), + errorStack: error instanceof Error ? error.stack : undefined, + commandNames, + }, + 'Error uninstalling shims', + ) + console.error(red(`Error uninstalling shims: ${error}`)) + process.exit(1) + } +} + +/** + * Handle the 'shims list' command + */ +export async function handleShimsList(): Promise { + try { + listShims() + } catch (error) { + logger.error( + { + errorMessage: error instanceof Error ? error.message : String(error), + errorStack: error instanceof Error ? error.stack : undefined, + }, + 'Error listing shims', + ) + console.error(red(`Error listing shims: ${error}`)) + process.exit(1) + } +} + +/** + * Handle the 'shims update' command + */ +export async function handleShimsUpdate( + commandNames?: string[], +): Promise { + try { + updateShims(commandNames) + } catch (error) { + logger.error( + { + errorMessage: error instanceof Error ? error.message : String(error), + errorStack: error instanceof Error ? error.stack : undefined, + commandNames, + }, + 'Error updating shims', + ) + console.error(red(`Error updating shims: ${error}`)) + process.exit(1) + } +} + +/** + * Handle the 'shims doctor' command + */ +export async function handleShimsDoctor(): Promise { + try { + doctorShims() + } catch (error) { + logger.error( + { + errorMessage: error instanceof Error ? error.message : String(error), + errorStack: error instanceof Error ? error.stack : undefined, + }, + 'Error running shims doctor', + ) + console.error(red(`Error running shims doctor: ${error}`)) + process.exit(1) + } +} + +/** + * Handle the 'shims upgrade' command + */ +export async function handleShimsUpgrade(): Promise { + try { + await upgradeShims() + } catch (error) { + logger.error( + { + errorMessage: error instanceof Error ? error.message : String(error), + errorStack: error instanceof Error ? error.stack : undefined, + }, + 'Error upgrading shims', + ) + console.error(red(`Error upgrading shims: ${error}`)) + process.exit(1) + } +} + + diff --git a/npm-app/src/index.ts b/npm-app/src/index.ts index 8dd2505a1d..57fbcbe9e0 100644 --- a/npm-app/src/index.ts +++ b/npm-app/src/index.ts @@ -11,7 +11,16 @@ import { cliArguments, cliOptions } from './cli-definitions' import { handlePublish } from './cli-handlers/publish' import { handleInitAgents } from './cli-handlers/init-agents' import { handleSaveAgent } from './cli-handlers/save-agent' -import { npmAppVersion, backendUrl } from './config' +import { + handleShimsInstall, + handleShimsUninstall, + handleShimsList, + handleShimsUpdate, + handleShimsDoctor, + handleShimsUpgrade, +} from './cli-handlers/shims' +import { generateEvalCommand } from './shell-dispatcher' +import { npmAppVersion } from './config' import { createTemplateProject } from './create-template-project' import { printModeLog, setPrintMode } from './display/print-mode' import { enableSquashNewlines } from './display/squash-newlines' @@ -128,6 +137,13 @@ Examples: $ codebuff --agent file-picker "find relevant files for authentication" $ codebuff --agent reviewer --params '{"focus": "security"}' "review this code" +Direct Commands (via shell shims): + $ codebuff shims install codebuff/base-lite@1.0.0 # One-step setup! + $ eval "$(codebuff shims env)" # Run this for immediate use + $ base-lite "fix the bug" # Direct command (after eval) + $ codebuff shims list # List installed shims + $ codebuff shims upgrade # Upgrade all shims to latest versions + For all commands and options, run 'codebuff' and then type 'help'. `, ) @@ -168,6 +184,46 @@ For all commands and options, run 'codebuff' and then type 'help'. process.exit(0) } + // Handle shims command + if (args[0] === 'shims') { + const subcommand = args[1] + const subArgs = args.slice(2) + + switch (subcommand) { + case 'install': + await handleShimsInstall(subArgs, { + force: options.force, + }) + break + case 'uninstall': + case 'remove': + await handleShimsUninstall(subArgs.length > 0 ? subArgs : undefined) + break + case 'list': + await handleShimsList() + break + case 'update': + await handleShimsUpdate(subArgs.length > 0 ? subArgs : undefined) + break + case 'doctor': + await handleShimsDoctor() + break + case 'upgrade': + await handleShimsUpgrade() + break + case 'env': + console.log(generateEvalCommand()) + break + default: + console.error(red(`Unknown shims subcommand: ${subcommand}`)) + console.log( + 'Available subcommands: install, uninstall, list, update, doctor, upgrade, env', + ) + process.exit(1) + } + process.exit(0) + } + // Handle deprecated --pro flag if (options.pro) { console.error( @@ -235,7 +291,7 @@ For all commands and options, run 'codebuff' and then type 'help'. const filteredArgs = args[0]?.startsWith('/$bunfs') ? args.slice(1) : args // If first arg is a command like 'publish' or 'save-agent', don't treat it as initial input - const isCommand = ['publish', 'init-agents', 'save-agent'].includes( + const isCommand = ['publish', 'init-agents', 'save-agent', 'shims'].includes( filteredArgs[0], ) const initialInput = isCommand ? '' : filteredArgs.join(' ') diff --git a/npm-app/src/shell-dispatcher.ts b/npm-app/src/shell-dispatcher.ts new file mode 100644 index 0000000000..e6840ee90d --- /dev/null +++ b/npm-app/src/shell-dispatcher.ts @@ -0,0 +1,939 @@ +import { + existsSync, + mkdirSync, + writeFileSync, + unlinkSync, + readdirSync, + readFileSync, +} from 'fs' +import path from 'path' +import os from 'os' +import { execSync } from 'child_process' + +import { yellow, green, red, cyan, bold } from 'picocolors' + +import { CONFIG_DIR } from './credentials' +import { createAuthHeaders } from './utils/auth-headers' +import { logger } from './utils/logger' + +const SHIMS_DIR = path.join(CONFIG_DIR, 'bin') +const WINDOWS_SHIMS_DIR = path.join( + os.homedir(), + 'AppData', + 'Local', + 'Manicode', + 'bin', +) + +/** + * Get the appropriate shims directory for the current platform + */ +export function getShimsDirectory(): string { + return process.platform === 'win32' ? WINDOWS_SHIMS_DIR : SHIMS_DIR +} + +/** + * Get the absolute path to the codebuff executable + */ +function getCodebuffPath(): string { + try { + if (process.platform === 'win32') { + return execSync('where codebuff', { encoding: 'utf8' }) + .trim() + .split('\n')[0] + } else { + return execSync('which codebuff', { encoding: 'utf8' }).trim() + } + } catch (error) { + // Fallback: assume codebuff is in PATH + return 'codebuff' + } +} + +/** + * Check if a command already exists in PATH + */ +function commandExists(command: string): string | null { + try { + if (process.platform === 'win32') { + const result = execSync(`where "${command}"`, { encoding: 'utf8' }).trim() + return result.split('\n')[0] + } else { + return execSync(`command -v "${command}"`, { encoding: 'utf8' }).trim() + } + } catch { + return null + } +} + +/** + * Parse a fully qualified agent ID to extract the agent name + * @param agentId Format: publisher/agent-id@version + * @returns The agent-id part or null if invalid format + */ +function parseAgentId(agentId: string): string | null { + const match = agentId.match(/^([^/]+)\/([^@]+)@(.+)$/) + return match ? match[2] : null +} + +/** + * Validate fully qualified agent ID format + */ +function validateAgentId(agentId: string): boolean { + return /^[^/]+\/[^@]+@.+$/.test(agentId) +} + +/** + * Validate command name for security + */ +function validateCommandName(name: string): boolean { + return /^[a-z0-9][a-z0-9-]{0,63}$/.test(name) +} + +/** + * Generate shim content for Unix shells (bash/zsh) + */ +function generateUnixShim( + commandName: string, + agentId: string, + codebuffPath: string, +): string { + return `#!/bin/sh +# Auto-generated Codebuff shim for '${commandName}' → ${agentId} +# Do not edit manually - use 'codebuff shims' commands +exec "${codebuffPath}" --agent "${agentId}" "$@" +` +} + +/** + * Generate shim content for Windows CMD + */ +function generateWindowsShim( + commandName: string, + agentId: string, + codebuffPath: string, +): string { + return `@echo off +REM Auto-generated Codebuff shim for '${commandName}' → ${agentId} +REM Do not edit manually - use 'codebuff shims' commands +"${codebuffPath}" --agent "${agentId}" %* +` +} + +/** + * Create a shim file for a specific agent ID and command name + */ +function createShim( + agentId: string, + commandName: string, + codebuffPath: string, + force: boolean, +): void { + if (!validateAgentId(agentId)) { + throw new Error( + `Invalid agent ID format: ${agentId}. Must be in format: publisher/agent-id@version`, + ) + } + + if (!validateCommandName(commandName)) { + throw new Error( + `Invalid command name: ${commandName}. Must be alphanumeric with hyphens, 1-64 characters.`, + ) + } + + const shimsDir = getShimsDirectory() + mkdirSync(shimsDir, { recursive: true }) + + // Check for conflicts + const existingCommand = commandExists(commandName) + if (existingCommand && !force) { + const shimPath = + process.platform === 'win32' + ? path.join(shimsDir, `${commandName}.cmd`) + : path.join(shimsDir, commandName) + + // Allow if it's our own shim + if (existingCommand !== shimPath) { + throw new Error( + `Command '${commandName}' already exists at: ${existingCommand}\n` + + 'Use --force to overwrite or choose a different name.', + ) + } + } + + if (process.platform === 'win32') { + const shimPath = path.join(shimsDir, `${commandName}.cmd`) + const content = generateWindowsShim(commandName, agentId, codebuffPath) + writeFileSync(shimPath, content, 'utf8') + } else { + const shimPath = path.join(shimsDir, commandName) + const content = generateUnixShim(commandName, agentId, codebuffPath) + writeFileSync(shimPath, content, 'utf8') + // Make executable + execSync(`chmod +x "${shimPath}"`) + } +} + +/** + * Remove a shim file for a specific command name + */ +function removeShim(commandName: string): boolean { + const shimsDir = getShimsDirectory() + const shimPath = + process.platform === 'win32' + ? path.join(shimsDir, `${commandName}.cmd`) + : path.join(shimsDir, commandName) + + if (existsSync(shimPath)) { + // Verify it's our shim before deleting + try { + const content = readFileSync(shimPath, 'utf8') + if (content.includes('Auto-generated Codebuff shim')) { + unlinkSync(shimPath) + return true + } + } catch (error) { + logger.warn( + { + errorMessage: error instanceof Error ? error.message : String(error), + shimPath, + }, + 'Failed to read shim file for deletion', + ) + } + } + return false +} + +/** + * Install shims for specified agent IDs + * @param agentSpecs Array of agent specs, either "publisher/agent-id@version" or "publisher/agent-id@version:custom-command" + */ +export function installShims( + agentSpecs: string[], + options: { force?: boolean } = {}, +): void { + const codebuffPath = getCodebuffPath() + const { force = false } = options + + if (!agentSpecs || agentSpecs.length === 0) { + console.log(yellow('No agent IDs specified to install as shims.')) + console.log( + 'Usage: codebuff shims install [publisher/agent-id@version:custom-command] ...', + ) + return + } + + const shimsDir = getShimsDirectory() + let installed = 0 + let errors = 0 + + for (const agentSpec of agentSpecs) { + try { + // Parse agentSpec - could be "publisher/agent-id@version" or "publisher/agent-id@version:custom-command" + const [agentId, customCommand] = agentSpec.split(':') + + // Extract command name from agent ID or use custom command + const defaultCommandName = parseAgentId(agentId) + if (!defaultCommandName) { + throw new Error( + `Invalid agent ID format: ${agentId}. Must be: publisher/agent-id@version`, + ) + } + + const commandName = customCommand || defaultCommandName + + createShim(agentId, commandName, codebuffPath, force) + console.log(green(`✓ ${commandName} → ${agentId}`)) + installed++ + } catch (error) { + console.error(red(`Error creating shim for '${agentSpec}': ${error}`)) + errors++ + } + } + + console.log( + green( + `\n✓ Installed ${installed} shim${installed !== 1 ? 's' : ''} in ${shimsDir}`, + ), + ) + if (errors > 0) { + console.log( + red(`✗ Failed to install ${errors} shim${errors !== 1 ? 's' : ''}`), + ) + } // Always add to PATH after successful installation + if (installed > 0) { + console.log('\n' + bold('Adding shims to PATH...')) + const success = addToPath() + + if (success) { + console.log( + '\n' + + green( + '🎉 Setup complete! Shims will persist after terminal restart.', + ), + ) + console.log('\n' + cyan('For immediate use in this session, run:')) + console.log(cyan(` eval "$(codebuff shims env)"`)) + console.log('\n' + cyan('Then try:')) + // Show the first installed command as an example + const firstCommand = agentSpecs[0] + const [agentId, customCommand] = firstCommand.split(':') + const defaultCommandName = parseAgentId(agentId) + const exampleCommand = customCommand || defaultCommandName + if (exampleCommand) { + console.log(cyan(` ${exampleCommand} "your prompt here"`)) + } + } else { + console.log( + '\n' + yellow('Profile setup failed. Use manual instructions below:'), + ) + showPathInstructions(shimsDir) + } + } +} + +/** + * Uninstall shims for specified command names (or all if none specified) + */ +export function uninstallShims(commandNames?: string[]): void { + const shimsDir = getShimsDirectory() + + if (!existsSync(shimsDir)) { + console.log(yellow('No shims directory found.')) + return + } + + let removed = 0 + + if (commandNames && commandNames.length > 0) { + // Remove specific command names + for (const commandName of commandNames) { + if (removeShim(commandName)) { + removed++ + } + } + } else { + // Remove all Codebuff shims + const files = readdirSync(shimsDir) + for (const file of files) { + const filePath = path.join(shimsDir, file) + try { + const content = readFileSync(filePath, 'utf8') + if (content.includes('Auto-generated Codebuff shim')) { + unlinkSync(filePath) + removed++ + } + } catch (error) { + // Skip files we can't read + } + } + } + + console.log(green(`✓ Removed ${removed} shim${removed !== 1 ? 's' : ''}`)) +} + +/** + * List all installed shims + */ +export function listShims(): void { + const shimsDir = getShimsDirectory() + + if (!existsSync(shimsDir)) { + console.log(yellow('No shims directory found.')) + return + } + + const files = readdirSync(shimsDir) + const shims: Array<{ + commandName: string + agentId?: string + filePath: string + }> = [] + + for (const file of files) { + const filePath = path.join(shimsDir, file) + try { + const content = readFileSync(filePath, 'utf8') + if (content.includes('Auto-generated Codebuff shim')) { + // Extract command name from filename + const commandName = + process.platform === 'win32' && file.endsWith('.cmd') + ? file.slice(0, -4) + : file + + // Try to extract agent ID from shim content + const agentMatch = content.match(/→ ([^\s]+)/) + const agentId = agentMatch ? agentMatch[1] : undefined + + shims.push({ commandName, agentId, filePath }) + } + } catch (error) { + // Skip files we can't read + } + } + + if (shims.length === 0) { + console.log(yellow('No Codebuff shims found.')) + return + } + + console.log(bold('Installed Codebuff shims:')) + const maxCommandLength = Math.max(...shims.map((s) => s.commandName.length)) + + for (const shim of shims.sort((a, b) => + a.commandName.localeCompare(b.commandName), + )) { + const padding = '.'.repeat(maxCommandLength - shim.commandName.length + 3) + const target = shim.agentId || 'unknown agent' + console.log(`${cyan(shim.commandName)} ${padding} ${target}`) + } + + console.log() + console.log(`Shims directory: ${shimsDir}`) +} + +/** + * Update shims (reinstall with current codebuff path) + */ +export function updateShims(commandNames?: string[]): void { + const shimsDir = getShimsDirectory() + + if (!existsSync(shimsDir)) { + console.log(yellow('No shims directory found. Use "install" first.')) + return + } + + // Get currently installed shims with their agent IDs + const files = readdirSync(shimsDir) + const installedShims: Array<{ commandName: string; agentId: string }> = [] + + for (const file of files) { + const filePath = path.join(shimsDir, file) + try { + const content = readFileSync(filePath, 'utf8') + if (content.includes('Auto-generated Codebuff shim')) { + const commandName = + process.platform === 'win32' && file.endsWith('.cmd') + ? file.slice(0, -4) + : file + + // Extract agent ID from shim content + const agentMatch = content.match(/→ ([^\s]+)/) + if (agentMatch) { + installedShims.push({ commandName, agentId: agentMatch[1] }) + } + } + } catch (error) { + // Skip files we can't read + } + } + + // Filter to specified command names or use all installed ones + const targetShims = commandNames + ? installedShims.filter((s) => commandNames.includes(s.commandName)) + : installedShims + + if (targetShims.length === 0) { + console.log(yellow('No shims to update.')) + return + } + + const codebuffPath = getCodebuffPath() + let updated = 0 + let errors = 0 + + for (const { commandName, agentId } of targetShims) { + try { + createShim(agentId, commandName, codebuffPath, true) + console.log(green(`✓ Updated ${commandName} → ${agentId}`)) + updated++ + } catch (error) { + console.error(red(`Error updating shim for '${commandName}': ${error}`)) + errors++ + } + } + + console.log(green(`\n✓ Updated ${updated} shim${updated !== 1 ? 's' : ''}`)) + if (errors > 0) { + console.log( + red(`✗ Failed to update ${errors} shim${errors !== 1 ? 's' : ''}`), + ) + } +} + +/** + * Doctor command to check shim health and PATH + */ +export function doctorShims(): void { + const shimsDir = getShimsDirectory() + + console.log(bold('Codebuff Shims Doctor\n')) + + // Check if shims directory exists + if (!existsSync(shimsDir)) { + console.log(red('✗ Shims directory does not exist')) + console.log(` Expected: ${shimsDir}`) + console.log( + ' Run: codebuff shims install ...', + ) + return + } + + console.log(green(`✓ Shims directory exists: ${shimsDir}`)) + + // Check PATH + const pathEnv = process.env.PATH || '' + const pathDirs = pathEnv.split(process.platform === 'win32' ? ';' : ':') + const shimsInPath = pathDirs.includes(shimsDir) + + if (shimsInPath) { + console.log(green('✓ Shims directory is in PATH')) + } else { + console.log(red('✗ Shims directory is NOT in PATH')) + console.log(' Add this to your shell profile:') + showPathInstructions(shimsDir) + } + + // Check installed shims + console.log('\nInstalled shims:') + const files = readdirSync(shimsDir) + const installedShims: string[] = [] + + for (const file of files) { + const filePath = path.join(shimsDir, file) + try { + const content = readFileSync(filePath, 'utf8') + if (content.includes('Auto-generated Codebuff shim')) { + const alias = + process.platform === 'win32' && file.endsWith('.cmd') + ? file.slice(0, -4) + : file + installedShims.push(alias) + } + } catch (error) { + // Skip files we can't read + } + } + + if (installedShims.length === 0) { + console.log(yellow(' No shims installed')) + return + } + + for (const alias of installedShims.sort()) { + const resolvedCommand = commandExists(alias) + const expectedShimPath = + process.platform === 'win32' + ? path.join(shimsDir, `${alias}.cmd`) + : path.join(shimsDir, alias) + + if (resolvedCommand === expectedShimPath) { + console.log(green(`✓ ${alias} → working`)) + } else if (resolvedCommand) { + console.log(yellow(`⚠ ${alias} conflicts with: ${resolvedCommand}`)) + } else { + console.log(red(`✗ ${alias} shim not found or not in PATH`)) + } + } +} + +/** + * Fetch the latest version of an agent from the store + */ +async function fetchLatestAgentVersion( + publisherId: string, + agentId: string, +): Promise { + try { + const url = `${process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com'}/api/agents/${publisherId}/${agentId}/latest` + const headers = createAuthHeaders() + + const response = await fetch(url, { + method: 'GET', + headers, + }) + + if (!response.ok) { + logger.warn( + { + publisherId, + agentId, + status: response.status, + statusText: response.statusText, + }, + 'Failed to fetch latest agent version', + ) + return null + } + + const data = await response.json() + return data.version || null + } catch (error) { + logger.error( + { + publisherId, + agentId, + errorMessage: error instanceof Error ? error.message : String(error), + }, + 'Error fetching latest agent version', + ) + return null + } +} + +/** + * Show platform-specific PATH setup instructions + */ +function showPathInstructions(shimsDir: string): void { + const { shell, evalCommand } = detectShell() + + console.log('\n' + bold('Quick setup options:')) + + // Option 1: For current session only + console.log('\n' + cyan('1. For current session only:')) + const sessionCmd = evalCommand.replace('{dir}', shimsDir) + console.log(cyan(` ${sessionCmd}`)) + + // Option 2: Permanent setup + console.log('\n' + cyan('2. Add to PATH permanently:')) + console.log(cyan(' codebuff shims path add')) + + // Option 3: Manual setup + console.log('\n' + cyan('3. Manual setup:')) + if (process.platform === 'win32') { + console.log(' For Command Prompt:') + console.log(cyan(` setx PATH "%PATH%;${shimsDir}"`)) + console.log(' For PowerShell (add to your profile):') + console.log(cyan(` $env:PATH += ";${shimsDir}"`)) + } else { + if (shell === 'fish') { + console.log(` Add to ~/.config/fish/config.fish:`) + console.log(cyan(` fish_add_path "${shimsDir}"`)) + } else { + const profileFile = shell === 'zsh' ? '~/.zshrc' : '~/.bashrc' + console.log(` Add to ${profileFile}:`) + console.log(cyan(` export PATH="${shimsDir}:$PATH"`)) + } + } + + console.log('\n' + yellow('After setup, you can run shims directly:')) + console.log( + cyan( + ' "your prompt" # instead of: codebuff "your prompt"', + ), + ) +} + +/** + * Detect the current shell and return shell info + */ +function detectShell(): { + shell: string + profileFile: string | null + evalCommand: string +} { + const shell = process.env.SHELL || '' + const isWindows = process.platform === 'win32' + + if (isWindows) { + // Windows: prefer PowerShell over CMD + const isPS = process.env.PSModulePath !== undefined + return { + shell: isPS ? 'powershell' : 'cmd', + profileFile: isPS ? '$PROFILE' : null, + evalCommand: isPS ? '$env:PATH += ";{dir}"' : 'set PATH=%PATH%;{dir}', + } + } + + // Unix-like systems + if (shell.includes('fish')) { + return { + shell: 'fish', + profileFile: path.join(os.homedir(), '.config/fish/config.fish'), + evalCommand: 'set -gx PATH "{dir}" $PATH', + } + } else if (shell.includes('zsh')) { + return { + shell: 'zsh', + profileFile: path.join(os.homedir(), '.zshrc'), + evalCommand: 'export PATH="{dir}:$PATH"', + } + } else { + // Default to bash + return { + shell: 'bash', + profileFile: path.join(os.homedir(), '.bashrc'), + evalCommand: 'export PATH="{dir}:$PATH"', + } + } +} + +/** + * Generate the eval command for current session + */ +export function generateEvalCommand(): string { + const shimsDir = getShimsDirectory() + const { evalCommand } = detectShell() + return evalCommand.replace('{dir}', shimsDir) +} + +/** + * Check if shims directory is already in PATH + */ +function isShimsDirInPath(): boolean { + const shimsDir = getShimsDirectory() + const pathEnv = process.env.PATH || '' + const pathDirs = pathEnv.split(process.platform === 'win32' ? ';' : ':') + return pathDirs.includes(shimsDir) +} + +/** + * Add shims directory to shell profile with idempotency + */ +export function addToPath(options: { force?: boolean } = {}): boolean { + const { force = false } = options + const shimsDir = getShimsDirectory() + const { shell, profileFile } = detectShell() + + if (!profileFile) { + console.log( + yellow(`Cannot auto-edit profile for ${shell}. Use manual setup.`), + ) + return false + } + + // Check if already in PATH + if (isShimsDirInPath() && !force) { + console.log(green('✓ Shims directory is already in PATH')) + return true + } + + try { + const profilePath = profileFile.replace('$PROFILE', getPowerShellProfile()) + + // Read existing profile or create empty + let profileContent = '' + if (existsSync(profilePath)) { + profileContent = readFileSync(profilePath, 'utf8') + + // Check if our entry already exists + if (profileContent.includes('# >>> codebuff shims >>>') && !force) { + console.log(green('✓ Shims already configured in profile')) + return true + } + } else { + // Create profile directory if it doesn't exist + mkdirSync(path.dirname(profilePath), { recursive: true }) + } + + // Generate the appropriate addition based on shell + let addition = '' + if (shell === 'fish') { + addition = `\n# >>> codebuff shims >>>\nfish_add_path "${shimsDir}"\n# <<< codebuff shims <<<\n` + } else if (shell === 'zsh') { + addition = `\n# >>> codebuff shims >>>\n# Codebuff agent shims\nexport PATH="${shimsDir}:$PATH"\n# <<< codebuff shims <<<\n` + } else if (shell === 'bash') { + addition = `\n# >>> codebuff shims >>>\n# Codebuff agent shims\nexport PATH="${shimsDir}:$PATH"\n# <<< codebuff shims <<<\n` + } else if (shell === 'powershell') { + addition = `\n# >>> codebuff shims >>>\n# Codebuff agent shims\n$env:PATH += ";${shimsDir}"\n# <<< codebuff shims <<<\n` + } + + // Remove existing codebuff section if it exists + const cleanContent = profileContent.replace( + /\n?# >>> codebuff shims >>>.*?# <<< codebuff shims <<<\n?/gs, + '', + ) + + // Add our section + const newContent = cleanContent + addition + writeFileSync(profilePath, newContent, 'utf8') + + console.log(green(`✓ Added shims to PATH in ${profilePath}`)) + return true + } catch (error) { + console.error(red(`Failed to update profile: ${error}`)) + return false + } +} + +/** + * Remove shims directory from shell profile + */ +export function removeFromPath(): boolean { + const { shell, profileFile } = detectShell() + + if (!profileFile) { + console.log( + yellow(`Cannot auto-edit profile for ${shell}. Use manual removal.`), + ) + return false + } + + try { + const profilePath = profileFile.replace('$PROFILE', getPowerShellProfile()) + + if (!existsSync(profilePath)) { + console.log(yellow('Profile file does not exist')) + return false + } + + const profileContent = readFileSync(profilePath, 'utf8') + + // Remove codebuff section + const cleanContent = profileContent.replace( + /\n?# >>> codebuff shims >>>.*?# <<< codebuff shims <<<\n?/gs, + '', + ) + + if (cleanContent === profileContent) { + console.log(yellow('No codebuff configuration found in profile')) + return false + } + + writeFileSync(profilePath, cleanContent, 'utf8') + console.log(green(`✓ Removed shims from PATH in ${profilePath}`)) + return true + } catch (error) { + console.error(red(`Failed to update profile: ${error}`)) + return false + } +} + +/** + * Get PowerShell profile path + */ +function getPowerShellProfile(): string { + try { + const result = execSync('powershell -Command "$PROFILE"', { + encoding: 'utf8', + }) + return result.trim() + } catch { + // Fallback to common location + return path.join( + os.homedir(), + 'Documents', + 'PowerShell', + 'Microsoft.PowerShell_profile.ps1', + ) + } +} + +/** + * Upgrade all installed shims to their latest versions + */ +export async function upgradeShims(): Promise { + const shimsDir = getShimsDirectory() + + if (!existsSync(shimsDir)) { + console.log(yellow('No shims directory found. Use "install" first.')) + return + } + + // Get currently installed shims with their agent IDs + const files = readdirSync(shimsDir) + const installedShims: Array<{ commandName: string; agentId: string }> = [] + + for (const file of files) { + const filePath = path.join(shimsDir, file) + try { + const content = readFileSync(filePath, 'utf8') + if (content.includes('Auto-generated Codebuff shim')) { + const commandName = + process.platform === 'win32' && file.endsWith('.cmd') + ? file.slice(0, -4) + : file + + // Extract agent ID from shim content + const agentMatch = content.match(/→ ([^\s]+)/) + if (agentMatch) { + installedShims.push({ commandName, agentId: agentMatch[1] }) + } + } + } catch (error) { + // Skip files we can't read + } + } + + if (installedShims.length === 0) { + console.log(yellow('No shims found to upgrade.')) + return + } + + console.log( + bold( + `Checking for updates to ${installedShims.length} shim${installedShims.length !== 1 ? 's' : ''}...`, + ), + ) + + const codebuffPath = getCodebuffPath() + let upgraded = 0 + let upToDate = 0 + let errors = 0 + + for (const { commandName, agentId } of installedShims) { + try { + // Parse the current agent ID to get publisher/agent/version + const match = agentId.match(/^([^/]+)\/([^@]+)@(.+)$/) + if (!match) { + console.log( + yellow(`⚠ ${commandName}: Invalid agent ID format (${agentId})`), + ) + continue + } + + const [, publisherId, agentName, currentVersion] = match + + // Fetch latest version + const latestVersion = await fetchLatestAgentVersion( + publisherId, + agentName, + ) + + if (!latestVersion) { + console.log(red(`✗ ${commandName}: Could not fetch latest version`)) + errors++ + continue + } + + if (latestVersion === currentVersion) { + console.log(green(`✓ ${commandName}: Up to date (${currentVersion})`)) + upToDate++ + continue + } + + // Upgrade the shim + const newAgentId = `${publisherId}/${agentName}@${latestVersion}` + createShim(newAgentId, commandName, codebuffPath, true) + console.log( + cyan(`↗ ${commandName}: ${currentVersion} → ${latestVersion}`), + ) + upgraded++ + } catch (error) { + console.error(red(`Error upgrading shim '${commandName}': ${error}`)) + errors++ + } + } + + console.log() + if (upgraded > 0) { + console.log( + green( + `✓ Upgraded ${upgraded} shim${upgraded !== 1 ? 's' : ''} to latest version${upgraded !== 1 ? 's' : ''}`, + ), + ) + } + if (upToDate > 0) { + console.log( + green( + `✓ ${upToDate} shim${upToDate !== 1 ? 's' : ''} already up to date`, + ), + ) + } + if (errors > 0) { + console.log( + red(`✗ Failed to upgrade ${errors} shim${errors !== 1 ? 's' : ''}`), + ) + } +} From 018ba60ab8b997e07f891e6b5eee10ec218e0e08 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Sun, 21 Sep 2025 19:02:28 -0700 Subject: [PATCH 3/5] tweaks --- npm-app/src/cli-handlers/shims.ts | 4 +- npm-app/src/shell-dispatcher.ts | 63 +++++++------------------------ 2 files changed, 14 insertions(+), 53 deletions(-) diff --git a/npm-app/src/cli-handlers/shims.ts b/npm-app/src/cli-handlers/shims.ts index 0ebf18ede2..b4cc550398 100644 --- a/npm-app/src/cli-handlers/shims.ts +++ b/npm-app/src/cli-handlers/shims.ts @@ -1,4 +1,4 @@ -import { red, green, yellow } from 'picocolors' +import { red } from 'picocolors' import { installShims, @@ -141,5 +141,3 @@ export async function handleShimsUpgrade(): Promise { process.exit(1) } } - - diff --git a/npm-app/src/shell-dispatcher.ts b/npm-app/src/shell-dispatcher.ts index e6840ee90d..e5f0d90ce5 100644 --- a/npm-app/src/shell-dispatcher.ts +++ b/npm-app/src/shell-dispatcher.ts @@ -1,3 +1,4 @@ +import { execSync } from 'child_process' import { existsSync, mkdirSync, @@ -6,9 +7,8 @@ import { readdirSync, readFileSync, } from 'fs' -import path from 'path' import os from 'os' -import { execSync } from 'child_process' +import path from 'path' import { yellow, green, red, cyan, bold } from 'picocolors' @@ -16,38 +16,13 @@ import { CONFIG_DIR } from './credentials' import { createAuthHeaders } from './utils/auth-headers' import { logger } from './utils/logger' -const SHIMS_DIR = path.join(CONFIG_DIR, 'bin') -const WINDOWS_SHIMS_DIR = path.join( - os.homedir(), - 'AppData', - 'Local', - 'Manicode', - 'bin', -) +const SHIMS_DIR = path.join(CONFIG_DIR, 'shims') /** * Get the appropriate shims directory for the current platform */ export function getShimsDirectory(): string { - return process.platform === 'win32' ? WINDOWS_SHIMS_DIR : SHIMS_DIR -} - -/** - * Get the absolute path to the codebuff executable - */ -function getCodebuffPath(): string { - try { - if (process.platform === 'win32') { - return execSync('where codebuff', { encoding: 'utf8' }) - .trim() - .split('\n')[0] - } else { - return execSync('which codebuff', { encoding: 'utf8' }).trim() - } - } catch (error) { - // Fallback: assume codebuff is in PATH - return 'codebuff' - } + return SHIMS_DIR } /** @@ -93,30 +68,22 @@ function validateCommandName(name: string): boolean { /** * Generate shim content for Unix shells (bash/zsh) */ -function generateUnixShim( - commandName: string, - agentId: string, - codebuffPath: string, -): string { +function generateUnixShim(commandName: string, agentId: string): string { return `#!/bin/sh # Auto-generated Codebuff shim for '${commandName}' → ${agentId} # Do not edit manually - use 'codebuff shims' commands -exec "${codebuffPath}" --agent "${agentId}" "$@" +exec codebuff --agent "${agentId}" "$@" ` } /** * Generate shim content for Windows CMD */ -function generateWindowsShim( - commandName: string, - agentId: string, - codebuffPath: string, -): string { +function generateWindowsShim(commandName: string, agentId: string): string { return `@echo off REM Auto-generated Codebuff shim for '${commandName}' → ${agentId} REM Do not edit manually - use 'codebuff shims' commands -"${codebuffPath}" --agent "${agentId}" %* +codebuff --agent "${agentId}" %* ` } @@ -126,7 +93,6 @@ REM Do not edit manually - use 'codebuff shims' commands function createShim( agentId: string, commandName: string, - codebuffPath: string, force: boolean, ): void { if (!validateAgentId(agentId)) { @@ -163,11 +129,11 @@ function createShim( if (process.platform === 'win32') { const shimPath = path.join(shimsDir, `${commandName}.cmd`) - const content = generateWindowsShim(commandName, agentId, codebuffPath) + const content = generateWindowsShim(commandName, agentId) writeFileSync(shimPath, content, 'utf8') } else { const shimPath = path.join(shimsDir, commandName) - const content = generateUnixShim(commandName, agentId, codebuffPath) + const content = generateUnixShim(commandName, agentId) writeFileSync(shimPath, content, 'utf8') // Make executable execSync(`chmod +x "${shimPath}"`) @@ -213,7 +179,6 @@ export function installShims( agentSpecs: string[], options: { force?: boolean } = {}, ): void { - const codebuffPath = getCodebuffPath() const { force = false } = options if (!agentSpecs || agentSpecs.length === 0) { @@ -243,7 +208,7 @@ export function installShims( const commandName = customCommand || defaultCommandName - createShim(agentId, commandName, codebuffPath, force) + createShim(agentId, commandName, force) console.log(green(`✓ ${commandName} → ${agentId}`)) installed++ } catch (error) { @@ -439,13 +404,12 @@ export function updateShims(commandNames?: string[]): void { return } - const codebuffPath = getCodebuffPath() let updated = 0 let errors = 0 for (const { commandName, agentId } of targetShims) { try { - createShim(agentId, commandName, codebuffPath, true) + createShim(agentId, commandName, true) console.log(green(`✓ Updated ${commandName} → ${agentId}`)) updated++ } catch (error) { @@ -867,7 +831,6 @@ export async function upgradeShims(): Promise { ), ) - const codebuffPath = getCodebuffPath() let upgraded = 0 let upToDate = 0 let errors = 0 @@ -905,7 +868,7 @@ export async function upgradeShims(): Promise { // Upgrade the shim const newAgentId = `${publisherId}/${agentName}@${latestVersion}` - createShim(newAgentId, commandName, codebuffPath, true) + createShim(newAgentId, commandName, true) console.log( cyan(`↗ ${commandName}: ${currentVersion} → ${latestVersion}`), ) From 2297cb8b1f3580bdd9270301c79803b0504b12df Mon Sep 17 00:00:00 2001 From: James Grugett Date: Sun, 21 Sep 2025 19:12:36 -0700 Subject: [PATCH 4/5] tweaks --- npm-app/src/shell-dispatcher.ts | 61 +++++++++++++++------------------ 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/npm-app/src/shell-dispatcher.ts b/npm-app/src/shell-dispatcher.ts index e5f0d90ce5..da6ccd6ed9 100644 --- a/npm-app/src/shell-dispatcher.ts +++ b/npm-app/src/shell-dispatcher.ts @@ -189,7 +189,6 @@ export function installShims( return } - const shimsDir = getShimsDirectory() let installed = 0 let errors = 0 @@ -209,51 +208,47 @@ export function installShims( const commandName = customCommand || defaultCommandName createShim(agentId, commandName, force) - console.log(green(`✓ ${commandName} → ${agentId}`)) installed++ + // Only show command name, not full agent ID + console.log( + green( + `✓ ${commandName} saved as a shim for "codebuff --agent ${agentId}"`, + ), + ) } catch (error) { console.error(red(`Error creating shim for '${agentSpec}': ${error}`)) errors++ } } - - console.log( - green( - `\n✓ Installed ${installed} shim${installed !== 1 ? 's' : ''} in ${shimsDir}`, - ), - ) if (errors > 0) { console.log( red(`✗ Failed to install ${errors} shim${errors !== 1 ? 's' : ''}`), ) - } // Always add to PATH after successful installation + } + + // Always add to PATH after successful installation if (installed > 0) { - console.log('\n' + bold('Adding shims to PATH...')) const success = addToPath() if (success) { - console.log( - '\n' + - green( - '🎉 Setup complete! Shims will persist after terminal restart.', - ), - ) - console.log('\n' + cyan('For immediate use in this session, run:')) - console.log(cyan(` eval "$(codebuff shims env)"`)) - console.log('\n' + cyan('Then try:')) - // Show the first installed command as an example + console.log('\nRun this for immediate use:') + if (success !== 'ALREADY_IN_PATH') { + console.log(cyan(`eval "$(codebuff shims env)"`)) + } + + // Show example command const firstCommand = agentSpecs[0] const [agentId, customCommand] = firstCommand.split(':') const defaultCommandName = parseAgentId(agentId) const exampleCommand = customCommand || defaultCommandName if (exampleCommand) { - console.log(cyan(` ${exampleCommand} "your prompt here"`)) + console.log(cyan(`${exampleCommand} "your prompt"`)) } } else { - console.log( - '\n' + yellow('Profile setup failed. Use manual instructions below:'), - ) - showPathInstructions(shimsDir) + console.log(yellow('\nCould not auto-configure PATH. Run manually:')) + const { evalCommand } = detectShell() + const sessionCmd = evalCommand.replace('{dir}', getShimsDirectory()) + console.log(cyan(sessionCmd)) } } } @@ -655,7 +650,9 @@ function isShimsDirInPath(): boolean { /** * Add shims directory to shell profile with idempotency */ -export function addToPath(options: { force?: boolean } = {}): boolean { +export function addToPath( + options: { force?: boolean } = {}, +): boolean | 'ALREADY_IN_PATH' { const { force = false } = options const shimsDir = getShimsDirectory() const { shell, profileFile } = detectShell() @@ -667,10 +664,9 @@ export function addToPath(options: { force?: boolean } = {}): boolean { return false } - // Check if already in PATH + // Check if already in PATH (silent check) if (isShimsDirInPath() && !force) { - console.log(green('✓ Shims directory is already in PATH')) - return true + return 'ALREADY_IN_PATH' } try { @@ -681,10 +677,9 @@ export function addToPath(options: { force?: boolean } = {}): boolean { if (existsSync(profilePath)) { profileContent = readFileSync(profilePath, 'utf8') - // Check if our entry already exists + // Check if our entry already exists (silent check) if (profileContent.includes('# >>> codebuff shims >>>') && !force) { - console.log(green('✓ Shims already configured in profile')) - return true + return 'ALREADY_IN_PATH' } } else { // Create profile directory if it doesn't exist @@ -713,7 +708,6 @@ export function addToPath(options: { force?: boolean } = {}): boolean { const newContent = cleanContent + addition writeFileSync(profilePath, newContent, 'utf8') - console.log(green(`✓ Added shims to PATH in ${profilePath}`)) return true } catch (error) { console.error(red(`Failed to update profile: ${error}`)) @@ -771,6 +765,7 @@ function getPowerShellProfile(): string { try { const result = execSync('powershell -Command "$PROFILE"', { encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], // Suppress stderr }) return result.trim() } catch { From 99e5ee0d8326e736781e34af073c2695787b2d50 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Sun, 21 Sep 2025 19:16:40 -0700 Subject: [PATCH 5/5] reduce knoweldge update --- knowledge.md | 37 +++---------------------------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/knowledge.md b/knowledge.md index 5f6565a1c8..8444db0cd2 100644 --- a/knowledge.md +++ b/knowledge.md @@ -53,19 +53,14 @@ Codebuff is a tool for editing codebases via natural language instruction to Buf ## CLI Interface Features - ESC key to toggle menu or stop AI response -- CTRL+C to exit the application## Shell Shims (Direct Commands) +- CTRL+C to exit the application -Codebuff supports shell shims for direct command invocation without the `codebuff` prefix. +### Shell Shims (Direct Commands) -### Features +Codebuff supports shell shims for direct command invocation without the `codebuff` prefix. - **Cross-platform**: Works on Windows (CMD/PowerShell), macOS, and Linux (bash/zsh/fish) - **Store integration**: Uses fully qualified agent IDs from the agent store -- **Auto-naming**: Automatically uses the agent-id part as the command name -- **Custom names**: Optional custom command names via colon syntax -- **Safe installation**: Creates executable files in `~/.config/manicode/bin` (Unix) or `%USERPROFILE%/AppData/Local/Manicode/bin` (Windows) -- **PATH integration**: Provides instructions to add shims directory to shell PATH -- **Conflict detection**: Warns about existing commands with same names - **Easy management**: Install, update, list, and uninstall shims via CLI commands### Quick Start (Recommended) ```bash @@ -79,32 +74,6 @@ eval "$(codebuff shims env)" base-lite "fix this bug" # Works right away! ``` -### Management Commands - -```bash -codebuff shims list # List installed shims -codebuff shims upgrade # Upgrade all shims to latest versions -codebuff shims doctor # Check shim health and PATH -codebuff shims env # Get eval command for current session -codebuff shims uninstall # Remove all shims -``` - -### Agent ID Format - -Agent IDs must be fully qualified store IDs: - -- Format: `publisher/agent-id@version` -- Example: `codebuff/base-lite@1.0.0` -- Custom command: `codebuff/base-lite@1.0.0:fast` - -### Benefits of New Setup - -- **One command setup**: Install automatically adds to PATH -- **Shell detection**: Automatically detects bash/zsh/fish/PowerShell -- **Safe editing**: Creates backups and uses idempotent markers -- **Immediate use**: `eval` commands work without restarting terminal -- **Cross-platform**: Works on macOS, Linux, and Windows - ## Package Management - Use Bun for all package management operations