@necto-ai/pgit
Version:
Private file tracking with dual git repositories
432 lines • 17.2 kB
JavaScript
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("commander");
const fs_1 = require("fs");
const path_1 = require("path");
const init_command_1 = require("./commands/init.command");
const status_command_1 = require("./commands/status.command");
const add_command_1 = require("./commands/add.command");
const commit_command_1 = require("./commands/commit.command");
const gitops_command_1 = require("./commands/gitops.command");
const cleanup_command_1 = require("./commands/cleanup.command");
const reset_command_1 = require("./commands/reset.command");
const preset_command_1 = require("./commands/preset.command");
const enhanced_error_handler_1 = require("./errors/enhanced.error-handler");
const logger_service_1 = require("./utils/logger.service");
/**
* Main CLI entry point
*/
async function main() {
// Read version from package.json
let version = '1.0.0'; // fallback version
try {
const packageJsonPath = (0, path_1.join)(__dirname, '..', 'package.json');
const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf-8'));
version = packageJson.version;
}
catch {
logger_service_1.logger.warn('Could not read package.json for version, using fallback');
}
commander_1.program
.name('pgit')
.description('Private Git Tracking CLI - Manage private files with dual repositories')
.version(version, '-v, -V, --version', 'Output the current version')
.option('--verbose', 'Show verbose output')
.on('option:verbose', () => {
logger_service_1.logger.setLevel(logger_service_1.LogLevel.DEBUG);
logger_service_1.logger.debug('Verbose mode enabled');
});
// Initialize command
commander_1.program
.command('init')
.description('Initialize private git tracking in current directory')
.action(async (options) => {
try {
const initCommand = new init_command_1.InitCommand();
const result = await initCommand.execute({ verbose: options.verbose });
if (result.success) {
logger_service_1.logger.success(result.message || 'Private git tracking initialized successfully');
}
else {
logger_service_1.logger.error(result.message || 'Failed to initialize private git tracking');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error, 'init');
}
});
// Status command
commander_1.program
.command('status')
.description('Show status of both main and private repositories')
.action(async (options) => {
try {
const statusCommand = new status_command_1.StatusCommand();
const result = await statusCommand.execute({ verbose: options.verbose });
if (result.success) {
logger_service_1.logger.info(result.message || 'Status retrieved successfully');
}
else {
logger_service_1.logger.error(result.message || 'Failed to get status');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Private status command (detailed private repo status only)
commander_1.program
.command('private-status')
.description('Show detailed status of private repository only')
.action(async (options) => {
try {
const statusCommand = new status_command_1.StatusCommand();
const result = await statusCommand.executePrivateOnly({ verbose: options.verbose });
if (result.success) {
logger_service_1.logger.info(result.message || 'Private status retrieved successfully');
}
else {
logger_service_1.logger.error(result.message || 'Failed to get private status');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Add command
commander_1.program
.command('add <path...>')
.description('Add file(s) or directory(ies) to private tracking')
.action(async (paths, options) => {
try {
const addCommand = new add_command_1.AddCommand();
const result = await addCommand.execute(paths, { verbose: options.verbose });
if (result.success) {
logger_service_1.logger.success(result.message || 'Files added to private tracking successfully');
}
else {
logger_service_1.logger.error(result.message || 'Failed to add files to private tracking');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Commit command
commander_1.program
.command('commit')
.description('Commit changes to private repository')
.option('-m, --message <message>', 'Commit message')
.action(async (options) => {
try {
const commitCommand = new commit_command_1.CommitCommand();
const result = await commitCommand.execute(options.message, { verbose: options.verbose });
if (result.success) {
logger_service_1.logger.success(result.message || 'Changes committed to private repository successfully');
}
else {
logger_service_1.logger.error(result.message || 'Failed to commit changes to private repository');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Preset commands
const presetCmd = commander_1.program
.command('preset')
.description('Manage file presets for common workflows (apply|define|undefine|list|show)');
presetCmd
.command('apply <preset-name>')
.description('Apply a preset by adding all its paths to private tracking')
.action(async (presetName, options) => {
try {
const presetCommand = new preset_command_1.PresetCommand();
const result = await presetCommand.apply(presetName, {
verbose: options.parent?.parent?.verbose || false,
});
if (result.success) {
// Success message is handled by the command itself
}
else {
logger_service_1.logger.error(result.message || 'Failed to apply preset');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
presetCmd
.command('define <preset-name> <paths...>')
.description('Define a new user preset with specified paths')
.option('-g, --global', 'Create a global preset (available across all projects)')
.action(async (presetName, paths, options) => {
try {
const presetCommand = new preset_command_1.PresetCommand();
const result = await presetCommand.define(presetName, paths, {
verbose: options.parent?.parent?.verbose || false,
global: options.global || false,
});
if (result.success) {
// Success message is handled by the command itself
}
else {
logger_service_1.logger.error(result.message || 'Failed to define preset');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
presetCmd
.command('undefine <preset-name>')
.description('Remove a user-defined preset')
.option('-g, --global', 'Remove a global preset')
.action(async (presetName, options) => {
try {
const presetCommand = new preset_command_1.PresetCommand();
const result = await presetCommand.undefine(presetName, {
verbose: options.parent?.parent?.verbose || false,
global: options.global || false,
});
if (result.success) {
// Success message is handled by the command itself
}
else {
logger_service_1.logger.error(result.message || 'Failed to remove preset');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
presetCmd
.command('list')
.description('List all available presets')
.action(async (options) => {
try {
const presetCommand = new preset_command_1.PresetCommand();
const result = await presetCommand.list({
verbose: options.parent?.parent?.verbose || false,
});
if (!result.success) {
logger_service_1.logger.error(result.message || 'Failed to list presets');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
presetCmd
.command('show <preset-name>')
.description('Show details about a specific preset')
.action(async (presetName, options) => {
try {
const presetCommand = new preset_command_1.PresetCommand();
const result = await presetCommand.show(presetName, {
verbose: options.parent?.parent?.verbose || false,
});
if (!result.success) {
logger_service_1.logger.error(result.message || 'Failed to show preset');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Git log command
commander_1.program
.command('log')
.description('Show commit history of private repository')
.option('-n, --max-count <number>', 'Limit number of commits', '10')
.option('--oneline', 'Show each commit on a single line')
.action(async (options) => {
try {
const gitOpsCommand = new gitops_command_1.GitOpsCommand();
const result = await gitOpsCommand.log({
maxCount: parseInt(options.maxCount) || 10,
oneline: options.oneline,
}, { verbose: options.verbose });
if (!result.success) {
logger_service_1.logger.error(result.message || 'Failed to get commit history');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Git add-changes command
commander_1.program
.command('add-changes')
.description('Stage changes in private repository')
.option('-A, --all', 'Stage all changes')
.action(async (options) => {
try {
const gitOpsCommand = new gitops_command_1.GitOpsCommand();
const result = await gitOpsCommand.addChanges(options.all, { verbose: options.verbose });
if (result.success) {
logger_service_1.logger.success(result.message || 'Changes staged successfully');
}
else {
logger_service_1.logger.error(result.message || 'Failed to stage changes');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Git diff command
commander_1.program
.command('diff')
.description('Show differences in private repository')
.option('--cached', 'Show staged changes')
.option('--name-only', 'Show only file names')
.action(async (options) => {
try {
const gitOpsCommand = new gitops_command_1.GitOpsCommand();
const result = await gitOpsCommand.diff({
cached: options.cached,
nameOnly: options.nameOnly,
}, { verbose: options.verbose });
if (!result.success) {
logger_service_1.logger.error(result.message || 'Failed to get differences');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Git branch command
commander_1.program
.command('branch [name]')
.description('List or create branches in private repository')
.option('-b, --create', 'Create new branch')
.action(async (name, options) => {
try {
const gitOpsCommand = new gitops_command_1.GitOpsCommand();
const result = await gitOpsCommand.branch(name, options.create, {
verbose: options.verbose,
});
if (result.success && name && options.create) {
logger_service_1.logger.success(result.message || 'Branch created successfully');
}
else if (!result.success) {
logger_service_1.logger.error(result.message || 'Failed to perform branch operation');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Git checkout command
commander_1.program
.command('checkout <target>')
.description('Switch branches or restore files in private repository')
.action(async (target, options) => {
try {
const gitOpsCommand = new gitops_command_1.GitOpsCommand();
const result = await gitOpsCommand.checkout(target, { verbose: options.verbose });
if (result.success) {
logger_service_1.logger.success(result.message || 'Checkout completed successfully');
}
else {
logger_service_1.logger.error(result.message || 'Failed to checkout');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Cleanup command
commander_1.program
.command('cleanup')
.description('Fix and repair private git tracking system')
.option('--force', 'Force cleanup operations')
.action(async (options) => {
try {
const cleanupCommand = new cleanup_command_1.CleanupCommand();
const result = await cleanupCommand.execute(options.force, { verbose: options.verbose });
if (result.success) {
logger_service_1.logger.success(result.message || 'Cleanup completed successfully');
}
else {
logger_service_1.logger.error(result.message || 'Cleanup completed with issues');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Reset command
commander_1.program
.command('reset')
.description('Completely remove pgit setup and restore all tracked files to main repository')
.option('--force', 'Skip confirmation prompt')
.option('--dry-run', 'Show what would be done without executing')
.action(async (options) => {
try {
const resetCommand = new reset_command_1.ResetCommand();
const result = await resetCommand.execute(options.force, {
verbose: options.verbose,
dryRun: options.dryRun,
});
if (result.success) {
logger_service_1.logger.success(result.message || 'Reset completed successfully');
}
else {
logger_service_1.logger.error(result.message || 'Reset failed');
process.exit(result.exitCode);
}
}
catch (error) {
handleError(error);
}
});
// Handle help command specially to ensure proper exit codes
const args = process.argv.slice(2);
if (args.includes('--help') || args.includes('-h') || (args.length === 1 && args[0] === 'help')) {
commander_1.program.outputHelp();
process.exit(0);
}
// Global error handler - only override for actual errors, not help/version
commander_1.program.exitOverride((err) => {
// Allow normal exit codes for help and version commands
if (err.code === 'commander.version' || err.code === 'commander.helpDisplayed') {
process.exit(0);
}
// Force exit code 1 for other errors
process.exit(1);
});
// Parse command line arguments
await commander_1.program.parseAsync(process.argv);
}
/**
* Handle errors with enhanced formatting and recovery suggestions
*/
function handleError(error, command) {
const context = enhanced_error_handler_1.EnhancedErrorHandler.createContext(command, [], process.cwd());
enhanced_error_handler_1.EnhancedErrorHandler.handleError(error, context);
process.exit(1);
}
// Run the CLI
if (require.main === module) {
main().catch(handleError);
}
//# sourceMappingURL=cli.js.map