gemini-mcp-tool
Version:
MCP server for Gemini CLI integration
736 lines (733 loc) โข 32.7 kB
JavaScript
import inquirer from "inquirer";
import chalk from "chalk";
import { exec } from "child_process";
import { promises as fs } from "fs";
import path from "path";
// Utility function to execute shell commands
const execAsync = (command) => {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
reject(new Error(`${error.message}\n${stderr}`));
}
else {
resolve(stdout.trim());
}
});
});
};
// Helper function to get error message from unknown error
const getErrorMessage = (error) => {
if (error instanceof Error) {
return error.message;
}
return String(error);
};
// Utility function to check if command exists
const commandExists = async (command) => {
try {
await execAsync(`command -v ${command}`);
return true;
}
catch {
return false;
}
};
// Progress indicator
const showProgress = (step, total, message) => {
console.log(chalk.cyan(`[${step}/${total}] ${message}`));
};
// Main menu options
const MENU_OPTIONS = [
{ name: "๐ Setup Environment", value: "setup" },
{ name: "๐ฟ Create Feature Branch", value: "branch" },
{ name: "๐จ Generate from Template", value: "create" },
{ name: "๐งช Test Changes", value: "test" },
{ name: "๐จ Format Code", value: "format" },
{ name: "๐ค Submit Contribution", value: "submit" },
{ name: "๐ Rollback/Undo", value: "undo" },
{ name: "๐ Validate System", value: "validate" },
{ name: "๐งช Mock Test Workflow", value: "mock-test" },
{ name: "๐ฌ Dry Run Workflow", value: "test-workflow" },
{ name: "โ Exit", value: "exit" },
];
// Template options
const TEMPLATE_OPTIONS = [
{ name: "๐ ๏ธ new-tool - Add a new MCP tool", value: "new-tool" },
{ name: "๐ bug-fix - Fix a bug", value: "bug-fix" },
{ name: "๐ docs - Improve documentation", value: "docs" },
{ name: "๐ฏ custom - Start from scratch", value: "custom" },
];
// Rollback options
const ROLLBACK_OPTIONS = [
{ name: "๐๏ธ Discard uncommitted changes", value: "discard" },
{ name: "โช Remove last commit (keep changes)", value: "soft-reset" },
{ name: "๐๏ธ Remove last commit and changes", value: "hard-reset" },
{
name: "๐จ Delete feature branch and return to main",
value: "delete-branch",
},
{ name: "โ Cancel", value: "cancel" },
];
class ContributionTUI {
constructor() { }
async showMainMenu() {
console.clear();
console.log(chalk.blue.bold("\n๐ฏ Gemini MCP Tool - Contribution System\n"));
const { choice } = await inquirer.prompt([
{
type: "list",
name: "choice",
message: "What would you like to do?",
choices: MENU_OPTIONS,
pageSize: 15,
},
]);
return choice;
}
async setupEnvironment() {
console.log(chalk.green.bold("\n๐ Setting up your contribution environment...\n"));
try {
// Check dependencies
showProgress(1, 5, "๐ Checking dependencies...");
const deps = [
{ cmd: "git", name: "Git", install: "git" },
{ cmd: "node", name: "Node.js", install: "node" },
{ cmd: "gh", name: "GitHub CLI", install: "gh" },
];
for (const dep of deps) {
if (!(await commandExists(dep.cmd))) {
console.log(chalk.yellow(`โ ๏ธ ${dep.name} not found, attempting to install...`));
await this.installDependency(dep.name, dep.install);
}
}
console.log(chalk.green("โ
All dependencies ready!"));
// Check GitHub authentication
showProgress(2, 5, "๐ Checking GitHub authentication...");
try {
await execAsync("gh auth status");
console.log(chalk.green("โ
GitHub CLI authenticated"));
}
catch {
console.log(chalk.yellow("๐ Please authenticate with GitHub:"));
await execAsync("gh auth login");
}
// Fork repository
showProgress(3, 5, "๐ด Forking repository...");
await execAsync("gh repo fork jamubc/gemini-mcp-tool --clone=false");
// Clone fork
showProgress(4, 5, "๐ฅ Cloning your fork...");
const username = await execAsync("gh api user --jq .login");
await execAsync(`git clone https://github.com/${username}/gemini-mcp-tool.git gemini-mcp-tool-contrib`);
// Setup upstream and install dependencies
showProgress(5, 5, "๐ Setting up upstream and installing dependencies...");
process.chdir("gemini-mcp-tool-contrib");
await execAsync("git remote add upstream https://github.com/jamubc/gemini-mcp-tool.git");
await execAsync("npm install");
console.log(chalk.green.bold("\nโ
Setup complete!"));
console.log(chalk.cyan("๐ Your contribution workspace: gemini-mcp-tool-contrib/"));
console.log(chalk.cyan("๐ Next steps:"));
console.log(chalk.cyan(" - Create a feature branch"));
console.log(chalk.cyan(" - Make your changes"));
console.log(chalk.cyan(" - Submit your contribution"));
}
catch (error) {
console.error(chalk.red(`โ Setup failed: ${getErrorMessage(error)}`));
}
}
async installDependency(name, installCmd) {
console.log(chalk.yellow(`๐ฅ Installing ${name}...`));
const packageManagers = [
{ check: "brew", cmd: `brew install ${installCmd}` },
{
check: "apt-get",
cmd: `sudo apt-get update && sudo apt-get install -y ${installCmd}`,
},
{ check: "yum", cmd: `sudo yum install -y ${installCmd}` },
{ check: "winget", cmd: `winget install ${installCmd}` },
];
for (const pm of packageManagers) {
if (await commandExists(pm.check)) {
await execAsync(pm.cmd);
return;
}
}
throw new Error(`Cannot auto-install ${name}. Please install manually.`);
}
async createFeatureBranch() {
console.log(chalk.green.bold("\n๐ฟ Creating Feature Branch\n"));
try {
const { featureName } = await inquirer.prompt([
{
type: "input",
name: "featureName",
message: "Enter feature name (e.g., add-calculator-tool):",
validate: (input) => input.length > 0 || "Feature name is required",
},
]);
const branchName = `feature/${featureName}`;
showProgress(1, 3, "๐ Switching to main and updating...");
await execAsync("git checkout main");
await execAsync("git pull upstream main");
showProgress(2, 3, `๐ฟ Creating branch: ${branchName}`);
await execAsync(`git checkout -b "${branchName}"`);
showProgress(3, 3, "โ
Branch ready!");
console.log(chalk.green(`โ
Ready to work on: ${branchName}`));
console.log(chalk.cyan("๐ ๏ธ Make your changes, then run the TUI again to test and submit"));
}
catch (error) {
console.error(chalk.red(`โ Branch creation failed: ${getErrorMessage(error)}`));
}
}
async generateFromTemplate() {
console.log(chalk.green.bold("\n๐จ Creating contribution from template...\n"));
try {
const { templateType } = await inquirer.prompt([
{
type: "list",
name: "templateType",
message: "Choose template:",
choices: TEMPLATE_OPTIONS,
},
]);
const { featureName } = await inquirer.prompt([
{
type: "input",
name: "featureName",
message: "Enter feature name (e.g., calculator-tool, fix-memory-leak):",
validate: (input) => input.length > 0 || "Feature name is required",
},
]);
// Create branch first
showProgress(1, 3, "๐ฟ Creating feature branch...");
const branchName = `feature/${featureName}`;
await execAsync("git checkout main");
await execAsync("git pull upstream main || true");
await execAsync(`git checkout -b "${branchName}"`);
// Generate template
showProgress(2, 3, "๐ Setting up template...");
if (templateType === "new-tool") {
await this.createNewToolTemplate(featureName);
}
else if (templateType === "bug-fix") {
await this.createBugFixTemplate();
}
else if (templateType === "docs") {
await this.createDocsTemplate();
}
else {
console.log(chalk.green("๐ฏ Custom contribution - you can start making changes directly"));
}
showProgress(3, 3, "๐ Template ready!");
console.log(chalk.green.bold("\n๐ Template ready!"));
console.log(chalk.cyan("Next steps:"));
console.log(chalk.cyan("1. Make your changes"));
console.log(chalk.cyan("2. Test with the TUI"));
console.log(chalk.cyan("3. Submit with the TUI"));
}
catch (error) {
console.error(chalk.red(`โ Template creation failed: ${getErrorMessage(error)}`));
}
}
async createNewToolTemplate(toolName) {
const { toolDescription } = await inquirer.prompt([
{
type: "input",
name: "toolDescription",
message: "Tool description:",
validate: (input) => input.length > 0 || "Description is required",
},
]);
// Read template
const templatePath = path.join("contribution", "templates", "new-tool.js");
const template = await fs.readFile(templatePath, "utf8");
// Replace placeholders
const toolContent = template
.replace(/\{\{TOOL_NAME\}\}/g, toolName)
.replace(/\{\{DESCRIPTION\}\}/g, toolDescription);
// Write tool file
const toolFilePath = path.join("src", `${toolName}-tool.ts`);
await fs.writeFile(toolFilePath, toolContent);
console.log(chalk.green(`โ
Created: ${toolFilePath}`));
console.log(chalk.cyan("๐ Edit the file to implement your tool logic"));
}
async createBugFixTemplate() {
const templatePath = path.join("contribution", "templates", "bug-fix.md");
const template = await fs.readFile(templatePath, "utf8");
await fs.writeFile("CONTRIBUTION_NOTES.md", template);
console.log(chalk.green("โ
Created: CONTRIBUTION_NOTES.md"));
console.log(chalk.cyan("๐ Fill out the template with details about your contribution"));
}
async createDocsTemplate() {
const templatePath = path.join("contribution", "templates", "docs-improvement.md");
const template = await fs.readFile(templatePath, "utf8");
await fs.writeFile("CONTRIBUTION_NOTES.md", template);
console.log(chalk.green("โ
Created: CONTRIBUTION_NOTES.md"));
console.log(chalk.cyan("๐ Fill out the template with details about your contribution"));
}
async testChanges() {
console.log(chalk.green.bold("\n๐งช Testing your changes...\n"));
try {
showProgress(1, 4, "๐จ Building...");
await execAsync("npm run build");
showProgress(2, 4, "โ
Checking TypeScript...");
await execAsync("npx tsc --noEmit");
showProgress(3, 4, "๐จ Checking code format...");
await this.formatCode();
showProgress(4, 4, "๐ Testing MCP server...");
try {
await execAsync("node dist/index.js --help > /dev/null");
console.log(chalk.green("โ
MCP server responds"));
}
catch {
console.log(chalk.red("โ MCP server test failed"));
throw new Error("MCP server test failed");
}
console.log(chalk.green.bold("\nโ
All tests passed!"));
console.log(chalk.cyan("๐ค Ready to submit? Use the submit option in the main menu"));
}
catch (error) {
console.error(chalk.red(`โ Tests failed: ${getErrorMessage(error)}`));
}
}
async formatCode() {
console.log(chalk.green.bold("\n๐จ Formatting code...\n"));
try {
// Check if prettier is available
if (await commandExists("npx")) {
try {
console.log(chalk.cyan("โจ Running Prettier..."));
await execAsync('npx prettier --write "src/**/*.{js,ts,json}" "*.{js,ts,json,md}" 2>/dev/null || true');
}
catch {
console.log(chalk.yellow("โ ๏ธ Some files couldn't be formatted"));
}
}
else {
console.log(chalk.yellow("โน๏ธ Prettier not available, skipping formatting"));
}
// Check if there's a lint script
try {
const packageJson = JSON.parse(await fs.readFile("package.json", "utf8"));
if (packageJson.scripts?.lint) {
console.log(chalk.cyan("๐ Running linter..."));
await execAsync("npm run lint --silent 2>/dev/null || true");
}
}
catch {
// Ignore if package.json doesn't exist or lint script is missing
}
console.log(chalk.green("โ
Code formatting complete!"));
}
catch (error) {
console.error(chalk.red(`โ Formatting failed: ${getErrorMessage(error)}`));
}
}
async submitContribution() {
console.log(chalk.green.bold("\n๐ค Submitting your contribution...\n"));
try {
// Auto-format first
showProgress(1, 4, "๐จ Formatting code...");
await this.formatCode();
// Process changes
showProgress(2, 4, "๐ Processing changes...");
const hasChanges = await this.checkForChanges();
if (hasChanges) {
console.log(chalk.cyan("Changes to be committed:"));
const status = await execAsync("git status --porcelain");
console.log(status);
const { commitMessage } = await inquirer.prompt([
{
type: "input",
name: "commitMessage",
message: "๐ฌ Enter commit message (or press Enter for auto-generated):",
default: "",
},
]);
let finalCommitMessage = commitMessage;
if (!commitMessage.trim()) {
const branchName = await execAsync("git branch --show-current");
const featureName = branchName.replace("feature/", "");
finalCommitMessage = `Add ${featureName}`;
}
await execAsync("git add -A");
await execAsync(`git commit -m "${finalCommitMessage}"`);
}
else {
console.log(chalk.yellow("โน๏ธ No changes to commit"));
}
// Push to fork
showProgress(3, 4, "๐ Pushing to your fork...");
const branchName = await execAsync("git branch --show-current");
await execAsync(`git push -u origin "${branchName}"`);
// Create PR
showProgress(4, 4, "๐ฏ Creating Pull Request...");
const featureName = branchName.replace("feature/", "");
const prTitle = `Add ${featureName}`;
const prBody = `## Summary
- Added ${featureName}
## Testing
- [x] Tested locally with TUI test functionality
- [x] Built successfully
- [x] MCP server functionality verified
## Checklist
- [x] Code follows project conventions
- [x] Changes are tested locally
- [x] Commit message is descriptive
---
๐ค Created with contribution automation TUI`;
await execAsync(`gh pr create --title "${prTitle}" --body "${prBody}" --base main --head "${branchName}"`);
console.log(chalk.green.bold("\nโ
Pull Request created successfully!"));
console.log(chalk.green("๐ Thank you for your contribution!"));
}
catch (error) {
console.error(chalk.red(`โ Submission failed: ${getErrorMessage(error)}`));
}
}
async checkForChanges() {
try {
await execAsync("git diff --cached --quiet");
await execAsync("git diff --quiet");
return false;
}
catch {
return true;
}
}
async rollbackChanges() {
console.log(chalk.green.bold("\n๐ Contribution rollback utility...\n"));
try {
const currentBranch = await execAsync("git branch --show-current");
if (!currentBranch.startsWith("feature/")) {
console.log(chalk.red("โ Not on a feature branch. Rollback only works on feature branches."));
console.log(chalk.yellow(`Current branch: ${currentBranch}`));
return;
}
console.log(chalk.cyan(`Current branch: ${currentBranch}\n`));
const { rollbackChoice } = await inquirer.prompt([
{
type: "list",
name: "rollbackChoice",
message: "Choose rollback option:",
choices: ROLLBACK_OPTIONS,
},
]);
switch (rollbackChoice) {
case "discard":
console.log(chalk.yellow("๐๏ธ Discarding uncommitted changes..."));
await execAsync("git checkout -- .");
await execAsync("git clean -fd");
console.log(chalk.green("โ
Uncommitted changes discarded"));
break;
case "soft-reset":
console.log(chalk.yellow("โช Removing last commit (keeping changes)..."));
await execAsync("git reset --soft HEAD~1");
console.log(chalk.green("โ
Last commit removed, changes kept in staging"));
break;
case "hard-reset":
console.log(chalk.yellow("๐๏ธ Removing last commit and changes..."));
await execAsync("git reset --hard HEAD~1");
console.log(chalk.green("โ
Last commit and changes removed"));
break;
case "delete-branch":
const { confirmDelete } = await inquirer.prompt([
{
type: "confirm",
name: "confirmDelete",
message: "๐จ This will delete the entire feature branch! Are you sure?",
default: false,
},
]);
if (confirmDelete) {
console.log(chalk.yellow("๐ Returning to main branch..."));
await execAsync("git checkout main");
console.log(chalk.yellow(`๐๏ธ Deleting feature branch: ${currentBranch}`));
await execAsync(`git branch -D "${currentBranch}"`);
console.log(chalk.green("โ
Feature branch deleted"));
}
else {
console.log(chalk.yellow("โ Cancelled"));
}
break;
case "cancel":
console.log(chalk.yellow("โ Cancelled"));
break;
}
console.log(chalk.green("\n๐ฏ Rollback complete!"));
}
catch (error) {
console.error(chalk.red(`โ Rollback failed: ${getErrorMessage(error)}`));
}
}
async validateSystem() {
console.log(chalk.green.bold("\n๐ Validating contribution system...\n"));
let errors = 0;
// Check dependencies
console.log(chalk.cyan("Checking dependencies..."));
const deps = ["git", "node", "npm", "gh"];
for (const dep of deps) {
if (await commandExists(dep)) {
console.log(chalk.green(`โ
${dep} is installed`));
}
else {
console.log(chalk.red(`โ ${dep} is missing`));
errors++;
}
}
// Check scripts
console.log(chalk.cyan("\nChecking scripts..."));
const scripts = [
"contribution/setup.sh",
"contribution/branch.sh",
"contribution/test.sh",
"contribution/submit.sh",
"contribution/create.sh",
];
for (const script of scripts) {
try {
const stats = await fs.stat(script);
if (stats.mode & 0o111) {
console.log(chalk.green(`โ
${script} is executable`));
}
else {
console.log(chalk.red(`โ ${script} is not executable`));
errors++;
}
}
catch {
console.log(chalk.red(`โ ${script} is missing`));
errors++;
}
}
// Check templates
console.log(chalk.cyan("\nChecking templates..."));
try {
await fs.stat("contribution/templates");
console.log(chalk.green("โ
Templates directory exists"));
const templates = ["new-tool.js", "bug-fix.md", "docs-improvement.md"];
for (const template of templates) {
try {
await fs.stat(`contribution/templates/${template}`);
console.log(chalk.green(`โ
${template} template exists`));
}
catch {
console.log(chalk.red(`โ ${template} template missing`));
errors++;
}
}
}
catch {
console.log(chalk.red("โ Templates directory missing"));
errors++;
}
// Check GitHub auth
console.log(chalk.cyan("\nTesting GitHub authentication..."));
try {
await execAsync("gh auth status");
console.log(chalk.green("โ
GitHub CLI authenticated"));
}
catch {
console.log(chalk.yellow("โ ๏ธ GitHub CLI not authenticated (run 'gh auth login')"));
}
// Check project structure
console.log(chalk.cyan("\nTesting project structure..."));
try {
await fs.stat("package.json");
console.log(chalk.green("โ
package.json found"));
}
catch {
console.log(chalk.red("โ package.json missing - not in project root?"));
errors++;
}
try {
await fs.stat("src");
console.log(chalk.green("โ
src directory found"));
}
catch {
console.log(chalk.red("โ src directory missing"));
errors++;
}
// Summary
console.log();
if (errors === 0) {
console.log(chalk.green.bold("๐ All validation checks passed!"));
console.log(chalk.green("โ
Contribution system is ready to use"));
}
else {
console.log(chalk.red.bold(`โ Found ${errors} issues`));
console.log(chalk.yellow("๐ง Please fix the issues above before using contribution tools"));
}
}
async mockTestWorkflow() {
console.log(chalk.green.bold("\n๐งช Running mock contribution test...\n"));
try {
// Save current state
const originalBranch = await execAsync("git branch --show-current");
console.log(chalk.cyan(`๐ Current branch: ${originalBranch}`));
// Test branch creation
console.log(chalk.cyan("\n1๏ธโฃ Testing branch creation..."));
await execAsync("git checkout main || true");
await execAsync("git pull upstream main || true");
await execAsync('git checkout -b "feature/test-mock-feature"');
// Create mock change
console.log(chalk.cyan("\n2๏ธโฃ Creating mock change..."));
await fs.writeFile("mock-test-file.js", "// Mock test file - safe to delete\nconsole.log('This is a test contribution');\n");
// Test build and validation
console.log(chalk.cyan("\n3๏ธโฃ Testing build and validation..."));
await execAsync("npm run build");
await execAsync("npx tsc --noEmit");
await execAsync("node dist/index.js --help > /dev/null");
// Test commit
console.log(chalk.cyan("\n4๏ธโฃ Testing commit process..."));
await execAsync("git add mock-test-file.js");
await execAsync('git commit -m "Add mock test file for contribution workflow validation"');
// Cleanup
console.log(chalk.cyan("\n5๏ธโฃ Cleaning up mock test..."));
await execAsync("git reset --hard HEAD~1");
await fs.unlink("mock-test-file.js").catch(() => { });
await execAsync(`git checkout ${originalBranch}`);
await execAsync('git branch -D "feature/test-mock-feature"');
console.log(chalk.green.bold("\n๐ Mock contribution test completed successfully!"));
console.log(chalk.green("โ
All contribution workflow components are working"));
console.log(chalk.cyan("\nThe contribution system is ready for real use!"));
}
catch (error) {
console.error(chalk.red(`โ Mock test failed: ${getErrorMessage(error)}`));
// Attempt cleanup
try {
await fs.unlink("mock-test-file.js").catch(() => { });
const currentBranch = await execAsync("git branch --show-current");
if (currentBranch === "feature/test-mock-feature") {
await execAsync("git checkout main");
await execAsync('git branch -d "feature/test-mock-feature" || git branch -D "feature/test-mock-feature"');
}
}
catch {
// Ignore cleanup errors
}
}
}
async dryRunWorkflow() {
console.log(chalk.green.bold("\n๐ฌ Testing contribution workflow (DRY RUN)...\n"));
try {
// Check GitHub CLI
if (!(await commandExists("gh"))) {
console.log(chalk.red("โ GitHub CLI (gh) is required but not installed."));
console.log(chalk.cyan("๐ฅ Install it from: https://cli.github.com/"));
return;
}
// Check auth
try {
await execAsync("gh auth status");
console.log(chalk.green("โ
GitHub CLI authenticated"));
}
catch {
console.log(chalk.yellow("๐ Please authenticate with GitHub first:"));
console.log(chalk.cyan(" gh auth login"));
return;
}
const username = await execAsync("gh api user --jq .login");
console.log(chalk.cyan(`๐ค GitHub user: ${username}`));
// Simulate workflow steps
console.log(chalk.cyan("๐ด Would fork: jamubc/gemini-mcp-tool"));
console.log(chalk.cyan(`๐ฅ Would clone: https://github.com/${username}/gemini-mcp-tool.git`));
console.log(chalk.cyan("๐ Would add upstream: https://github.com/jamubc/gemini-mcp-tool.git"));
try {
await fs.stat("package.json");
console.log(chalk.cyan("๐ฆ Would install dependencies with: npm install"));
}
catch {
console.log(chalk.yellow("โ ๏ธ No package.json found - would need to be in project directory"));
}
console.log(chalk.cyan("๐ฟ Would create feature branch: feature/test-feature"));
console.log(chalk.cyan("๐จ Would build project with: npm run build"));
console.log(chalk.cyan("โ
Would run TypeScript check: npx tsc --noEmit"));
console.log(chalk.cyan("๐ Would test MCP server functionality"));
console.log(chalk.cyan("๐ Would commit changes"));
console.log(chalk.cyan("๐ Would push to fork"));
console.log(chalk.cyan("๐ฏ Would create Pull Request"));
console.log(chalk.green.bold("\nโ
Workflow test complete! All steps would execute successfully."));
console.log(chalk.cyan("๐ To run actual workflow:"));
console.log(chalk.cyan(" - Use the setup option in the main menu"));
console.log(chalk.cyan(" - Create a feature branch"));
console.log(chalk.cyan(" - Make your changes"));
console.log(chalk.cyan(" - Test and submit"));
}
catch (error) {
console.error(chalk.red(`โ Dry run failed: ${getErrorMessage(error)}`));
}
}
async run() {
console.log(chalk.blue.bold("Welcome to the Gemini MCP Tool Contribution System! ๐ฏ"));
while (true) {
try {
const choice = await this.showMainMenu();
switch (choice) {
case "setup":
await this.setupEnvironment();
break;
case "branch":
await this.createFeatureBranch();
break;
case "create":
await this.generateFromTemplate();
break;
case "test":
await this.testChanges();
break;
case "format":
await this.formatCode();
break;
case "submit":
await this.submitContribution();
break;
case "undo":
await this.rollbackChanges();
break;
case "validate":
await this.validateSystem();
break;
case "mock-test":
await this.mockTestWorkflow();
break;
case "test-workflow":
await this.dryRunWorkflow();
break;
case "exit":
console.log(chalk.green.bold("\n๐ Thank you for contributing to Gemini MCP Tool!"));
process.exit(0);
break;
default:
console.log(chalk.red("Unknown option selected"));
}
// Wait for user to press enter before showing menu again
await inquirer.prompt([
{
type: "input",
name: "continue",
message: "Press Enter to continue...",
},
]);
}
catch (error) {
console.error(chalk.red(`Error: ${getErrorMessage(error)}`));
// Wait for user acknowledgment
await inquirer.prompt([
{
type: "input",
name: "continue",
message: "Press Enter to continue...",
},
]);
}
}
}
}
// Run the TUI
const tui = new ContributionTUI();
tui.run().catch((error) => {
console.error(chalk.red("Fatal error:", getErrorMessage(error)));
process.exit(1);
});
//# sourceMappingURL=contribute.js.map