UNPKG

pit-manager

Version:

Centralized prompt management system for Human Behavior AI agents

275 lines (234 loc) 8.59 kB
#\!/usr/bin/env node /** * PIT CLI - Node.js entry point * This allows the CLI to work without Python */ const { program } = require('commander'); const path = require('path'); const fs = require('fs'); const { ExecutionTracker } = require('../storage/execution-tracker'); const { VersioningOperations } = require('../versioning/operations'); const { BranchManager } = require('../versioning/branches'); // Version from package.json const packageJson = require('../../../package.json'); program .name('pit') .description('Git-like version control for AI prompts') .version(packageJson.version); program .command('init') .description('Initialize a new PIT repository') .option('--force', 'Reinitialize existing repository') .option('--online', 'Initialize with online storage backend') .action(async (options) => { try { const repoPath = '.pit'; const versioning = new VersioningOperations(repoPath); // Check if already initialized if (fs.existsSync(path.join(repoPath, 'refs', 'HEAD')) && \!options.force) { console.log(`PIT repository already exists in ${path.resolve(repoPath)}`); console.log('Use --force to reinitialize'); process.exit(1); } // Create repository structure await versioning.init(options.force); console.log(`Initialized empty PIT repository in ${path.resolve(repoPath)}`); console.log('\nCreated example files:'); console.log(' .pit/prompts/template.md - Example prompt template'); console.log(' example.js - JavaScript example using simplified API'); console.log(' example.ts - TypeScript example using simplified API'); if (options.online) { console.log('\nOnline mode: Configure your .env file with:'); console.log(' PIT_REPO_KEY=your-repo-key'); console.log(' PIT_SUPABASE_URL=your-supabase-url'); console.log(' PIT_SUPABASE_KEY=your-supabase-key'); } console.log('\nRun "pit --help" to see available commands'); } catch (error) { console.error('Error:', error.message); process.exit(1); } }); program .command('add <prompt>') .description('Add a prompt to the staging area') .option('-f, --file <file>', 'Read prompt from file') .action(async (prompt, options) => { try { const repoPath = '.pit'; const versioning = new VersioningOperations(repoPath); let content = prompt; if (options.file) { content = fs.readFileSync(options.file, 'utf8'); } const hash = await versioning.addPrompt([{ role: 'user', content }]); console.log(`Added prompt with hash: ${hash}`); } catch (error) { console.error('Error:', error.message); process.exit(1); } }); program .command('commit') .description('Commit staged prompts') .option('-m, --message <message>', 'Commit message') .action(async (options) => { try { const repoPath = '.pit'; const versioning = new VersioningOperations(repoPath); const message = options.message || 'Commit prompts'; const commitHash = await versioning.commit(message); console.log(`Created commit: ${commitHash}`); } catch (error) { console.error('Error:', error.message); process.exit(1); } }); program .command('log') .description('Show commit history') .option('-n, --limit <number>', 'Limit number of commits', '10') .action(async (options) => { try { const repoPath = '.pit'; const versioning = new VersioningOperations(repoPath); const history = await versioning.getHistory(parseInt(options.limit)); if (history.length === 0) { console.log('No commits yet'); return; } for (const commit of history) { console.log(`commit ${commit.hash}`); console.log(`Date: ${new Date(commit.timestamp).toLocaleString()}`); console.log(`\n ${commit.message}\n`); } } catch (error) { console.error('Error:', error.message); process.exit(1); } }); program .command('status') .description('Show repository status') .action(async () => { try { const repoPath = '.pit'; const versioning = new VersioningOperations(repoPath); const status = await versioning.status(); console.log('On branch:', status.branch || 'main'); if (status.staged && status.staged.length > 0) { console.log('\nChanges to be committed:'); for (const item of status.staged) { console.log(` ${item}`); } } if (status.modified && status.modified.length > 0) { console.log('\nModified files:'); for (const item of status.modified) { console.log(` ${item}`); } } if (\!status.staged?.length && \!status.modified?.length) { console.log('Nothing to commit, working tree clean'); } } catch (error) { console.error('Error:', error.message); process.exit(1); } }); program .command('branch [name]') .description('List or create branches') .option('-d, --delete', 'Delete branch') .action(async (name, options) => { try { const repoPath = '.pit'; const branchManager = new BranchManager(repoPath); if (\!name) { // List branches const branches = await branchManager.listBranches(); const current = await branchManager.getCurrentBranch(); for (const branch of branches) { const prefix = branch === current ? '* ' : ' '; console.log(`${prefix}${branch}`); } } else if (options.delete) { // Delete branch await branchManager.deleteBranch(name); console.log(`Deleted branch ${name}`); } else { // Create branch await branchManager.createBranch(name); console.log(`Created branch ${name}`); } } catch (error) { console.error('Error:', error.message); process.exit(1); } }); program .command('checkout <branch>') .description('Switch to a different branch') .action(async (branch) => { try { const repoPath = '.pit'; const branchManager = new BranchManager(repoPath); await branchManager.checkout(branch); console.log(`Switched to branch '${branch}'`); } catch (error) { console.error('Error:', error.message); process.exit(1); } }); program .command('analytics') .description('Show execution analytics') .option('--days <number>', 'Number of days to analyze', '7') .action(async (options) => { try { const repoPath = '.pit'; const tracker = new ExecutionTracker(repoPath); const executions = await tracker.getExecutions(); const days = parseInt(options.days); const cutoff = new Date(); cutoff.setDate(cutoff.getDate() - days); const recent = executions.filter(e => new Date(e.timestamp) > cutoff ); console.log(`Analytics for last ${days} days:`); console.log(`Total executions: ${recent.length}`); if (recent.length > 0) { const totalTokens = recent.reduce((sum, e) => sum + (e.tokens || 0), 0); const totalCost = recent.reduce((sum, e) => sum + (e.cost || 0), 0); console.log(`Total tokens: ${totalTokens.toLocaleString()}`); console.log(`Total cost: $${totalCost.toFixed(2)}`); // Group by model const byModel = {}; for (const exec of recent) { const model = exec.model || 'unknown'; if (\!byModel[model]) { byModel[model] = { count: 0, tokens: 0, cost: 0 }; } byModel[model].count++; byModel[model].tokens += exec.tokens || 0; byModel[model].cost += exec.cost || 0; } console.log('\nBy model:'); for (const [model, stats] of Object.entries(byModel)) { console.log(` ${model}:`); console.log(` Executions: ${stats.count}`); console.log(` Tokens: ${stats.tokens.toLocaleString()}`); console.log(` Cost: $${stats.cost.toFixed(2)}`); } } } catch (error) { console.error('Error:', error.message); process.exit(1); } }); // Parse command line arguments program.parse(process.argv); // Show help if no command provided if (\!process.argv.slice(2).length) { program.outputHelp(); }