#!/usr/bin/env node /** * cli:gitflow-status * Queries GitFlow status without modifying anything. * Reports current branch, branches with tracking, tags, worktrees, and actions. * * 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 { StatusResult } 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-status — SmartStack Studio Queries GitFlow status without modifying anything. Reports current branch, branches with tracking, tags, worktrees, and suggested actions. Usage: npx ts-node index.ts --spec '' [--workdir ] [--json] Options: --spec JSON string or path to a JSON file with status 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: StatusResult; try { result = await execute(validation.data, workdir); } catch (err) { const errResult = { success: false, currentBranch: 'unknown', branches: [], tags: [], workingTree: { isDirty: false, isRebasing: false, isMerging: false, isCherryPicking: false, unstagedCount: 0, stagedCount: 0, }, blockers: [(err as Error).message], warnings: validation.warnings, }; 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) { console.log(`Current branch: ${result.currentBranch}`); console.log(`Total branches: ${result.branches.length}`); console.log(`Total tags: ${result.tags.length}`); if (result.workingTree.isDirty) { console.log(`⚠ Working tree is dirty (${result.workingTree.stagedCount} staged, ${result.workingTree.unstagedCount} unstaged)`); } if (result.workingTree.isRebasing) { console.log('⚠ Rebase in progress'); } if (result.workingTree.isMerging) { console.log('⚠ Merge in progress'); } if (result.workingTree.isCherryPicking) { console.log('⚠ Cherry-pick in progress'); } if (result.actions && result.actions.length > 0) { console.log(`\nSuggested actions (${result.actions.length}):`); for (const action of result.actions.slice(0, 5)) { console.log(` [${action.priority.toUpperCase()}] ${action.action}`); } } } else { console.error(`Error: ${result.error}`); } } process.exit(result.success ? 0 : 2);