gopeak 2.2.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.
package/README.md CHANGED
@@ -54,7 +54,7 @@ gopeak
54
54
  }
55
55
  ```
56
56
 
57
- > `GOPEAK_TOOL_PROFILE=compact` is the default. It keeps prompt/tool-list token usage low while preserving access to the full toolset via catalog discovery.
57
+ > `GOPEAK_TOOL_PROFILE=compact` is the default. It exposes 33 core tools with 22 dynamic tool groups (78 additional tools) that activate on demand — keeping token usage low while preserving full capability.
58
58
 
59
59
  ### 3) First prompts to try
60
60
 
@@ -67,9 +67,9 @@ gopeak
67
67
  ## Why GoPeak
68
68
 
69
69
  - **Real project feedback loop**: run the game, inspect logs, and fix in-context.
70
- - **95+ tools available** across scene/script/resource/runtime/LSP/DAP/input/assets.
71
- - **Token-efficient by default**: compact tool surface for lower context overhead.
72
- - **Discoverability built-in**: use `tool.catalog` (alias of `tool_catalog`) to find hidden or legacy tools by keyword.
70
+ - **110+ tools available** across scene/script/resource/runtime/LSP/DAP/input/assets.
71
+ - **Token-efficient by default**: compact tool surface (33 tools) + dynamic tool groups. Only activate what you need — no more 110-tool context bombs.
72
+ - **Dynamic tool groups**: search with `tool.catalog` and matching groups auto-activate. Or manually activate with `tool.groups`.
73
73
  - **Deep Godot integration**: ClassDB queries, runtime inspection, debugger hooks, bridge-based scene/resource edits.
74
74
 
75
75
  ### Best For
@@ -84,8 +84,8 @@ gopeak
84
84
 
85
85
  GoPeak supports three exposure profiles:
86
86
 
87
- - `compact` (default): exposes a curated alias set (about 20 core tools)
88
- - `full`: exposes full legacy tool list (95+)
87
+ - `compact` (default): 33 core tools + **22 dynamic tool groups** (78 additional tools activated on demand)
88
+ - `full`: exposes full legacy tool list (110+)
89
89
  - `legacy`: same exposed behavior as `full`
90
90
 
91
91
  Configure with either:
@@ -93,18 +93,61 @@ Configure with either:
93
93
  - `GOPEAK_TOOL_PROFILE`
94
94
  - `MCP_TOOL_PROFILE` (fallback alias)
95
95
 
96
- ### How to discover hidden tools in compact mode
96
+ ### Dynamic Tool Groups (compact mode)
97
97
 
98
- Call:
98
+ In `compact` mode, 78 additional tools are organized into **22 groups** that activate automatically when needed:
99
99
 
100
- - `tool.catalog` (compact alias)
101
- - `tool_catalog` (legacy name)
102
-
103
- Example intent:
104
-
105
- > "Use `tool.catalog` with query `animation` and show relevant tools."
100
+ | Group | Tools | Description |
101
+ |---|---|---|
102
+ | `scene_advanced` | 3 | Duplicate, reparent nodes, load sprites |
103
+ | `uid` | 2 | UID management for resources |
104
+ | `import_export` | 5 | Import pipeline, reimport, validate project |
105
+ | `autoload` | 4 | Autoload singletons, main scene |
106
+ | `signal` | 2 | Disconnect signals, list connections |
107
+ | `runtime` | 4 | Live scene inspection, runtime properties, metrics |
108
+ | `resource` | 4 | Create/modify materials, shaders, resources |
109
+ | `animation` | 5 | Animations, tracks, animation tree, state machine |
110
+ | `plugin` | 3 | Enable/disable/list editor plugins |
111
+ | `input` | 1 | Input action mapping |
112
+ | `tilemap` | 2 | TileSet and TileMap cell painting |
113
+ | `audio` | 4 | Audio buses, effects, volume |
114
+ | `navigation` | 2 | Navigation regions and agents |
115
+ | `theme_ui` | 3 | Theme colors, font sizes, shaders |
116
+ | `asset_store` | 3 | Search/download CC0 assets |
117
+ | `testing` | 6 | Screenshots, viewport capture, input injection |
118
+ | `dx_tools` | 4 | Error log, project health, find usages, scaffold |
119
+ | `intent_tracking` | 9 | Intent capture, decision logs, handoff briefs |
120
+ | `class_advanced` | 1 | Class inheritance inspection |
121
+ | `lsp` | 3 | GDScript completions, hover, symbols |
122
+ | `dap` | 6 | Breakpoints, stepping, stack traces |
123
+ | `version_gate` | 2 | Version validation, patch verification |
124
+
125
+ **How it works:**
126
+
127
+ 1. **Auto-activation via catalog**: Search with `tool.catalog` and matching groups activate automatically.
128
+ > "Use `tool.catalog` with query `animation` and show relevant tools."
129
+
130
+ 2. **Manual activation**: Directly activate a group with `tool.groups`.
131
+ > "Use `tool.groups` to activate the `dap` group for debugging."
132
+
133
+ 3. **Deactivation**: Remove groups when done to reduce context.
134
+ > "Use `tool.groups` to reset all active groups."
135
+
136
+ The server sends `notifications/tools/list_changed` so MCP clients (Claude Code, Claude Desktop) automatically refresh the tool list.
137
+
138
+ ### Don't worry about tokens
139
+
140
+ GoPeak uses **cursor-based pagination** for `tools/list` — even in `full` profile, tools are delivered in pages (default 33) instead of dumping all 110+ definitions at once. Your AI client fetches the next page only when it needs more.
141
+
142
+ Set page size with `GOPEAK_TOOLS_PAGE_SIZE`:
106
143
 
107
- This lets assistants start with a small, efficient default surface but still find and use the full capability set when needed.
144
+ ```json
145
+ {
146
+ "env": {
147
+ "GOPEAK_TOOLS_PAGE_SIZE": "25"
148
+ }
149
+ }
150
+ ```
108
151
 
109
152
  ---
110
153
 
@@ -140,9 +183,38 @@ GoPeak also exposes two CLI bin names:
140
183
 
141
184
  ---
142
185
 
186
+ ## CI
187
+
188
+ GitHub Actions runs on push/PR and executes:
189
+
190
+ 1. `npm run build`
191
+ 2. `npx tsc --noEmit`
192
+ 3. `npm run test:ci`
193
+
194
+ Run the same checks locally:
195
+
196
+ ```bash
197
+ npm run ci
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Versioning & Release
203
+
204
+ Use the built-in bump script to keep `package.json` and `server.json` in sync:
205
+
206
+ ```bash
207
+ node scripts/bump-version.mjs patch
208
+ node scripts/bump-version.mjs minor --dry-run
209
+ ```
210
+
211
+ Full release checklist: [`docs/release-process.md`](docs/release-process.md).
212
+
213
+ ---
214
+
143
215
  ## Addons (Recommended)
144
216
 
145
- ### Auto Reload + Runtime Addon installer
217
+ ### Auto Reload + Editor Bridge + Runtime Addon installer
146
218
 
147
219
  Install in your Godot project folder:
148
220
 
@@ -156,7 +228,7 @@ PowerShell:
156
228
  iwr https://raw.githubusercontent.com/HaD0Yun/godot-mcp/main/install-addon.ps1 -UseBasicParsing | iex
157
229
  ```
158
230
 
159
- Then enable plugin in **Project Settings → Plugins**.
231
+ Then enable plugins in **Project Settings → Plugins** (especially `godot_mcp_editor` for bridge-backed scene/resource tools).
160
232
 
161
233
  ---
162
234
 
@@ -207,9 +279,11 @@ Visualize your entire project architecture with `visualizer.map` (`map_project`
207
279
  - "Press `ui_accept`, move mouse to (400, 300), click, then capture a screenshot."
208
280
  - "Inspect live scene tree and report nodes with missing scripts or invalid references."
209
281
 
210
- ### Discovery in compact mode
282
+ ### Discovery & dynamic groups
211
283
  - "Use `tool.catalog` with query `tilemap` and list the most relevant tools."
284
+ - "Activate the `dap` tool group for breakpoint debugging with `tool.groups`."
212
285
  - "Find import pipeline tools with `tool.catalog` query `import` and run the best one for texture settings."
286
+ - "Reset all active tool groups with `tool.groups` to reduce context."
213
287
 
214
288
  ---
215
289
 
@@ -222,23 +296,27 @@ Visualize your entire project architecture with `visualizer.map` (`map_project`
222
296
  | `GOPEAK_TOOL_PROFILE` | Tool exposure profile: `compact`, `full`, `legacy` | `compact` |
223
297
  | `MCP_TOOL_PROFILE` | Fallback profile env alias | `compact` |
224
298
  | `GODOT_PATH` | Explicit Godot executable path | auto-detect |
299
+ | `GODOT_BRIDGE_PORT` | Bridge/Visualizer HTTP+WS port override (aliases: `MCP_BRIDGE_PORT`, `GOPEAK_BRIDGE_PORT`) | `6505` |
225
300
  | `DEBUG` | Enable server debug logs (`true`/`false`) | `false` |
226
301
  | `LOG_MODE` | Recording mode: `lite` or `full` | `lite` |
302
+ | `GOPEAK_TOOLS_PAGE_SIZE` | Number of tools per `tools/list` page (pagination) | `33` |
303
+ | `GOPEAK_BRIDGE_PORT` | Port for unified bridge/visualizer server | `6505` |
304
+ | `GOPEAK_BRIDGE_HOST` | Bind host for bridge/visualizer server | `127.0.0.1` |
227
305
 
228
306
  ### Ports
229
307
 
230
308
  | Port | Service |
231
309
  |---|---|
232
- | `6505` | Unified Godot Bridge + Visualizer server (+ `/health`, `/mcp`) |
310
+ | `6505` (default) | Unified Godot Bridge + Visualizer server (+ `/health`, `/mcp`) on loopback by default |
233
311
  | `6005` | Godot LSP |
234
312
  | `6006` | Godot DAP |
235
313
  | `7777` | Runtime addon command socket (only needed for runtime tools) |
236
314
 
237
315
  ### Minimal port profiles
238
316
 
239
- - **Core editing only**: `6505`
240
- - **Core + runtime actions (screenshots/input/runtime inspect)**: `6505` + `7777`
241
- - **Full debugging + diagnostics**: `6505` + `6005` + `6006` + `7777`
317
+ - **Core editing only**: bridge port (`GODOT_BRIDGE_PORT`, default `6505`)
318
+ - **Core + runtime actions (screenshots/input/runtime inspect)**: bridge port + `7777`
319
+ - **Full debugging + diagnostics**: bridge port + `6005` + `6006` + `7777`
242
320
 
243
321
  ---
244
322
 
@@ -248,12 +326,14 @@ Visualize your entire project architecture with `visualizer.map` (`map_project`
248
326
  - **No MCP tools visible** → restart your MCP client
249
327
  - **Project path invalid** → confirm `project.godot` exists
250
328
  - **Runtime tools not working** → install/enable runtime addon plugin
251
- - **Need a tool that is not visible** → run `tool.catalog` and search by capability keyword
329
+ - **Need a tool that is not visible** → run `tool.catalog` to search and auto-activate matching groups, or use `tool.groups` to activate a specific group
252
330
 
253
331
  ---
254
332
 
255
333
  ## Docs & Project Links
256
334
 
335
+ - [Architecture (MCP Platform Direction)](docs/architecture.md)
336
+ - [Platform Roadmap (P1/P2/P3)](docs/platform-roadmap.md)
257
337
  - [CHANGELOG](CHANGELOG.md)
258
338
  - [ROADMAP](ROADMAP.md)
259
339
  - [CONTRIBUTING](CONTRIBUTING.md)
@@ -60,13 +60,30 @@ func _process(_delta: float) -> void:
60
60
  _handle_disconnect()
61
61
 
62
62
 
63
- func connect_to_server(url: String = DEFAULT_URL) -> void:
64
- server_url = url
63
+ func connect_to_server(url: String = "") -> void:
64
+ server_url = _resolve_server_url(url)
65
65
  _should_reconnect = true
66
66
  _current_reconnect_delay = RECONNECT_DELAY
67
67
  _attempt_connection()
68
68
 
69
69
 
70
+ func _resolve_server_url(explicit_url: String) -> String:
71
+ if explicit_url != "":
72
+ return explicit_url
73
+
74
+ var env_keys := ["GODOT_BRIDGE_PORT", "MCP_BRIDGE_PORT", "GOPEAK_BRIDGE_PORT"]
75
+ for key in env_keys:
76
+ var raw := OS.get_environment(key)
77
+ if raw == "":
78
+ continue
79
+ if raw.is_valid_int():
80
+ var port := int(raw)
81
+ if port >= 1 and port <= 65535:
82
+ return "ws://127.0.0.1:%d/godot" % port
83
+
84
+ return DEFAULT_URL
85
+
86
+
70
87
  func disconnect_from_server() -> void:
71
88
  _should_reconnect = false
72
89
  if _reconnect_timer:
@@ -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
+ }