#!/usr/bin/env node /** * cli:gitflow-abort * Detects and aborts in-progress git operations (rebase, merge, cherry-pick). * * Exit codes: 0 = OK, 1 = warnings, 2 = blockers * * Usage: * npx ts-node index.ts --spec '' [--workdir ] [--json] */ import { parseArgs } from 'node:util'; import { readFileSync, existsSync } from 'node:fs'; import { validate } from './validate.js'; import { execute } from './execute.js'; import type { AbortResult } from './types.js'; const { values, positionals } = parseArgs({ options: { spec: { type: 'string' }, workdir: { type: 'string' }, help: { type: 'boolean', short: 'h' }, json: { type: 'boolean' }, }, allowPositionals: true, }); if (values.help) { console.log(` cli:gitflow-abort — SmartStack Studio Detects and aborts in-progress git operations (rebase, merge, cherry-pick). Usage: npx ts-node index.ts --spec '' [--workdir ] [--json] Options: --spec JSON string or path to a JSON file with abort spec --workdir Working directory for git operations (defaults to cwd) --json Output result as JSON --help, -h Show this help `); process.exit(0); } // ── Validate args ───────────────────────────────────────── const specArg = values.spec; const workdir = values.workdir || process.cwd(); if (!specArg) { const result = { success: false, blockers: ['Missing required arg: --spec'], warnings: [] }; if (values.json) console.log(JSON.stringify(result, null, 2)); else console.error('[BLOCKER] Missing required arg: --spec'); process.exit(2); } // ── Parse spec (JSON string or file path) ────────────────── let rawSpec: unknown; try { if (existsSync(specArg)) { rawSpec = JSON.parse(readFileSync(specArg, 'utf-8')); } else { rawSpec = JSON.parse(specArg); } } catch (err) { const result = { success: false, blockers: [`Invalid JSON spec: ${(err as Error).message}`], warnings: [] }; if (values.json) console.log(JSON.stringify(result, null, 2)); else console.error(`[BLOCKER] Invalid JSON spec: ${(err as Error).message}`); process.exit(2); } // ── Validate ────────────────────────────────────────────── const validation = validate(rawSpec); if (!validation.valid || !validation.data) { const result = { success: false, blockers: validation.blockers, warnings: validation.warnings }; if (values.json) { console.log(JSON.stringify(result, null, 2)); } else { for (const b of validation.blockers) console.error(`[BLOCKER] ${b}`); for (const w of validation.warnings) console.warn(`[WARNING] ${w}`); } process.exit(2); } // Log warnings if (!values.json) { for (const w of validation.warnings) console.warn(`[WARNING] ${w}`); } // ── Execute ─────────────────────────────────────────────── let result: AbortResult; try { result = await execute(validation.data, workdir); } catch (err) { const errResult = { success: false, operation: 'none' as const, restored: false, error: (err as Error).message }; if (values.json) console.log(JSON.stringify(errResult, null, 2)); else console.error(`[BLOCKER] ${(err as Error).message}`); process.exit(2); } // ── Output ──────────────────────────────────────────────── if (values.json) { console.log(JSON.stringify(result, null, 2)); } else { if (result.success) { if (result.operation === 'none') { console.log('✓ No in-progress operation'); } else { console.log(`✓ ${result.operation} aborted successfully`); } } else { console.error(`✗ Failed to abort ${result.operation}: ${result.error}`); } } process.exit(result.success ? 0 : 2);