UNPKG

@awesomate/hosting-mcp

Version:

Awesomate MCP server — lets Claude manage your Awesomate WordPress hosting, plan, limits, n8n automations, and build Node/static apps + databases

103 lines (93 loc) • 3.33 kB
#!/usr/bin/env node /** * Version-control health check for an Awesomate app folder. * * Run from (or pass) the project directory. Prints a JSON report Claude uses * to keep the user's version control healthy WITHOUT them knowing Git: * - repo/remote wiring, uncommitted + unpushed work * - branch → deploy-env mapping (dev → awesomate.dev, main → live) * - last GitHub Actions deploy run (when `gh` is authed) * * Exit code 0 always (reporting tool, not a gate) — Claude decides what to * fix based on the JSON. */ import { execSync } from 'node:child_process'; import { existsSync } from 'node:fs'; import { resolve } from 'node:path'; const dir = resolve(process.argv[2] ?? '.'); const report = { dir, gitInstalled: false, repoInitialized: false, remote: null, branch: null, uncommittedFiles: 0, unpushedCommits: null, // null = unknown (no upstream) hasUpstream: false, envTracked: false, ghAuthed: false, lastDeployRun: null, problems: [], }; function sh(cmd) { return execSync(cmd, { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'], timeout: 15_000 }) .toString() .trim(); } function trySh(cmd) { try { return sh(cmd); } catch { return null; } } if (trySh('git --version') === null) { report.problems.push('git_not_installed'); } else { report.gitInstalled = true; report.repoInitialized = trySh('git rev-parse --is-inside-work-tree') === 'true'; if (!report.repoInitialized) { report.problems.push('no_repo'); } else { report.branch = trySh('git branch --show-current') || '(detached)'; report.remote = trySh('git remote get-url origin'); if (!report.remote) report.problems.push('no_remote'); const status = trySh('git status --porcelain') ?? ''; report.uncommittedFiles = status ? status.split('\n').length : 0; if (report.uncommittedFiles > 0) report.problems.push('uncommitted_changes'); // .env must never be tracked — the single most dangerous drift. const tracked = trySh('git ls-files .env */.env') ?? ''; report.envTracked = tracked.length > 0; if (report.envTracked) report.problems.push('env_file_tracked'); const upstream = trySh('git rev-parse --abbrev-ref @{upstream}'); report.hasUpstream = !!upstream; if (upstream) { const ahead = trySh('git rev-list --count @{upstream}..HEAD'); report.unpushedCommits = ahead === null ? null : Number(ahead); if (report.unpushedCommits > 0) report.problems.push('unpushed_commits'); } else if (report.remote) { report.problems.push('branch_not_pushed'); } if (report.remote && trySh('gh auth status') !== null) { report.ghAuthed = true; const runs = trySh( 'gh run list --limit 1 --json displayTitle,conclusion,headBranch,updatedAt', ); try { const parsed = JSON.parse(runs ?? '[]'); if (parsed[0]) { report.lastDeployRun = parsed[0]; if (parsed[0].conclusion && parsed[0].conclusion !== 'success') { report.problems.push('last_deploy_failed'); } } } catch { /* no runs / not parseable — fine */ } } } } if (!existsSync(resolve(dir, '.gitignore')) && report.repoInitialized) { report.problems.push('no_gitignore'); } console.log(JSON.stringify(report, null, 2));