synabun 1.3.0

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.
Files changed (187) hide show
  1. package/CHANGELOG.md +665 -0
  2. package/LICENSE +191 -0
  3. package/README.md +853 -0
  4. package/hooks/claude-code/post-plan.mjs +391 -0
  5. package/hooks/claude-code/post-remember.mjs +239 -0
  6. package/hooks/claude-code/pre-compact.mjs +241 -0
  7. package/hooks/claude-code/pre-websearch.mjs +90 -0
  8. package/hooks/claude-code/prompt-submit.mjs +861 -0
  9. package/hooks/claude-code/session-start.mjs +494 -0
  10. package/hooks/claude-code/shared.mjs +368 -0
  11. package/hooks/claude-code/stop.mjs +646 -0
  12. package/hooks/claude-code/test-user-learning.mjs +531 -0
  13. package/mcp-server/package.json +38 -0
  14. package/mcp-server/run.mjs +10 -0
  15. package/mcp-server/src/config.ts +70 -0
  16. package/mcp-server/src/http.ts +93 -0
  17. package/mcp-server/src/index.ts +203 -0
  18. package/mcp-server/src/preload-http.ts +25 -0
  19. package/mcp-server/src/preload.ts +29 -0
  20. package/mcp-server/src/scripts/migrate-qdrant-to-sqlite.ts +506 -0
  21. package/mcp-server/src/services/categories.ts +429 -0
  22. package/mcp-server/src/services/discord.ts +702 -0
  23. package/mcp-server/src/services/file-checksums.ts +33 -0
  24. package/mcp-server/src/services/local-embeddings.ts +77 -0
  25. package/mcp-server/src/services/neural-interface.ts +344 -0
  26. package/mcp-server/src/services/sqlite.ts +823 -0
  27. package/mcp-server/src/tools/browser-advanced.ts +123 -0
  28. package/mcp-server/src/tools/browser-interact.ts +255 -0
  29. package/mcp-server/src/tools/browser-navigate.ts +77 -0
  30. package/mcp-server/src/tools/browser-observe.ts +1330 -0
  31. package/mcp-server/src/tools/browser.ts +107 -0
  32. package/mcp-server/src/tools/card-tools.ts +193 -0
  33. package/mcp-server/src/tools/card.ts +22 -0
  34. package/mcp-server/src/tools/category.ts +379 -0
  35. package/mcp-server/src/tools/discord-channel.ts +274 -0
  36. package/mcp-server/src/tools/discord-guild.ts +188 -0
  37. package/mcp-server/src/tools/discord-member.ts +158 -0
  38. package/mcp-server/src/tools/discord-message.ts +231 -0
  39. package/mcp-server/src/tools/discord-onboarding.ts +228 -0
  40. package/mcp-server/src/tools/discord-role.ts +209 -0
  41. package/mcp-server/src/tools/discord-thread.ts +110 -0
  42. package/mcp-server/src/tools/discord-webhook.ts +176 -0
  43. package/mcp-server/src/tools/discord.ts +40 -0
  44. package/mcp-server/src/tools/forget.ts +38 -0
  45. package/mcp-server/src/tools/git-tools.ts +141 -0
  46. package/mcp-server/src/tools/git.ts +13 -0
  47. package/mcp-server/src/tools/image-tools.ts +98 -0
  48. package/mcp-server/src/tools/image.ts +17 -0
  49. package/mcp-server/src/tools/leonardo-browser-tools.ts +281 -0
  50. package/mcp-server/src/tools/leonardo.ts +28 -0
  51. package/mcp-server/src/tools/loop.ts +348 -0
  52. package/mcp-server/src/tools/memories.ts +118 -0
  53. package/mcp-server/src/tools/recall.ts +323 -0
  54. package/mcp-server/src/tools/reflect.ts +145 -0
  55. package/mcp-server/src/tools/remember.ts +115 -0
  56. package/mcp-server/src/tools/response.ts +26 -0
  57. package/mcp-server/src/tools/restore.ts +34 -0
  58. package/mcp-server/src/tools/sync.ts +114 -0
  59. package/mcp-server/src/tools/tictactoe-tools.ts +98 -0
  60. package/mcp-server/src/tools/tictactoe.ts +13 -0
  61. package/mcp-server/src/tools/utils.ts +25 -0
  62. package/mcp-server/src/tools/whiteboard-tools.ts +260 -0
  63. package/mcp-server/src/tools/whiteboard.ts +22 -0
  64. package/mcp-server/src/tui.ts +940 -0
  65. package/mcp-server/src/types.ts +85 -0
  66. package/mcp-server/tsconfig.json +19 -0
  67. package/neural-interface/package.json +27 -0
  68. package/neural-interface/public/apple-touch-icon.png +0 -0
  69. package/neural-interface/public/category-logos/claude.svg +1 -0
  70. package/neural-interface/public/category-logos/gemini-color.svg +1 -0
  71. package/neural-interface/public/category-logos/openai.svg +1 -0
  72. package/neural-interface/public/category-logos/synabun.png +0 -0
  73. package/neural-interface/public/claude-chat.css +878 -0
  74. package/neural-interface/public/claude-chat.html +92 -0
  75. package/neural-interface/public/claude-chat.js +1168 -0
  76. package/neural-interface/public/favicon-16x16.png +0 -0
  77. package/neural-interface/public/favicon-32x32.png +0 -0
  78. package/neural-interface/public/gallery.svg +6 -0
  79. package/neural-interface/public/icon-192.png +0 -0
  80. package/neural-interface/public/icon-512.png +0 -0
  81. package/neural-interface/public/index-legacy.html +14677 -0
  82. package/neural-interface/public/index.html +212 -0
  83. package/neural-interface/public/index2d-legacy.html +8193 -0
  84. package/neural-interface/public/index2d.html +127 -0
  85. package/neural-interface/public/invite.html +351 -0
  86. package/neural-interface/public/leonardoai.svg +1 -0
  87. package/neural-interface/public/logo-synabun.png +0 -0
  88. package/neural-interface/public/logoHD.png +0 -0
  89. package/neural-interface/public/manifest.json +14 -0
  90. package/neural-interface/public/offline.html +299 -0
  91. package/neural-interface/public/onboarding.html +2335 -0
  92. package/neural-interface/public/openclaw-logo-text.png +0 -0
  93. package/neural-interface/public/pin-svgrepo-com.svg +25 -0
  94. package/neural-interface/public/sessionicon.svg +8 -0
  95. package/neural-interface/public/shared/ansi-parser.js +305 -0
  96. package/neural-interface/public/shared/api.js +1043 -0
  97. package/neural-interface/public/shared/colors.js +153 -0
  98. package/neural-interface/public/shared/constants.js +141 -0
  99. package/neural-interface/public/shared/html-shell.js +777 -0
  100. package/neural-interface/public/shared/html-term-renderer.js +1022 -0
  101. package/neural-interface/public/shared/i18n.js +147 -0
  102. package/neural-interface/public/shared/registry.js +82 -0
  103. package/neural-interface/public/shared/state.js +86 -0
  104. package/neural-interface/public/shared/storage.js +150 -0
  105. package/neural-interface/public/shared/styles.css +21212 -0
  106. package/neural-interface/public/shared/ui-automation-studio.js +3573 -0
  107. package/neural-interface/public/shared/ui-bookmarks.js +231 -0
  108. package/neural-interface/public/shared/ui-claude-panel.js +6333 -0
  109. package/neural-interface/public/shared/ui-command-runner.js +534 -0
  110. package/neural-interface/public/shared/ui-cost-widget.js +602 -0
  111. package/neural-interface/public/shared/ui-detail.js +1454 -0
  112. package/neural-interface/public/shared/ui-explorer.js +1356 -0
  113. package/neural-interface/public/shared/ui-file-explorer.js +2750 -0
  114. package/neural-interface/public/shared/ui-games.js +411 -0
  115. package/neural-interface/public/shared/ui-help.js +107 -0
  116. package/neural-interface/public/shared/ui-image-gallery.js +461 -0
  117. package/neural-interface/public/shared/ui-invite.js +519 -0
  118. package/neural-interface/public/shared/ui-keybinds.js +452 -0
  119. package/neural-interface/public/shared/ui-layouts.js +297 -0
  120. package/neural-interface/public/shared/ui-link.js +731 -0
  121. package/neural-interface/public/shared/ui-loading.js +255 -0
  122. package/neural-interface/public/shared/ui-menubar.js +499 -0
  123. package/neural-interface/public/shared/ui-multiselect.js +93 -0
  124. package/neural-interface/public/shared/ui-navbar.js +222 -0
  125. package/neural-interface/public/shared/ui-notifications.js +287 -0
  126. package/neural-interface/public/shared/ui-panels.js +309 -0
  127. package/neural-interface/public/shared/ui-resume.js +653 -0
  128. package/neural-interface/public/shared/ui-search.js +145 -0
  129. package/neural-interface/public/shared/ui-sessions.js +531 -0
  130. package/neural-interface/public/shared/ui-settings.js +5987 -0
  131. package/neural-interface/public/shared/ui-sidebar.js +1905 -0
  132. package/neural-interface/public/shared/ui-skills.js +2805 -0
  133. package/neural-interface/public/shared/ui-stats.js +38 -0
  134. package/neural-interface/public/shared/ui-sync.js +201 -0
  135. package/neural-interface/public/shared/ui-terminal.js +6385 -0
  136. package/neural-interface/public/shared/ui-tooltip.js +123 -0
  137. package/neural-interface/public/shared/ui-trash.js +439 -0
  138. package/neural-interface/public/shared/ui-tutorial-draw.js +875 -0
  139. package/neural-interface/public/shared/ui-tutorial-steps.js +2276 -0
  140. package/neural-interface/public/shared/ui-tutorial.js +1117 -0
  141. package/neural-interface/public/shared/ui-update.js +244 -0
  142. package/neural-interface/public/shared/ui-whiteboard.js +3262 -0
  143. package/neural-interface/public/shared/ui-workspaces.js +722 -0
  144. package/neural-interface/public/shared/utils.js +360 -0
  145. package/neural-interface/public/shared/vterm-buffer.js +1102 -0
  146. package/neural-interface/public/sw.js +52 -0
  147. package/neural-interface/public/synabun.png +0 -0
  148. package/neural-interface/public/toolbar-album.svg +8 -0
  149. package/neural-interface/public/toolbar-question.svg +6 -0
  150. package/neural-interface/public/toolbar-sessions.svg +6 -0
  151. package/neural-interface/public/variant/2d/background.js +113 -0
  152. package/neural-interface/public/variant/2d/context-menu.js +133 -0
  153. package/neural-interface/public/variant/2d/gfx.js +45 -0
  154. package/neural-interface/public/variant/2d/graph.js +1635 -0
  155. package/neural-interface/public/variant/2d/hulls.js +207 -0
  156. package/neural-interface/public/variant/2d/lasso.js +135 -0
  157. package/neural-interface/public/variant/2d/layout.js +594 -0
  158. package/neural-interface/public/variant/2d/main.js +521 -0
  159. package/neural-interface/public/variant/2d/minimap.js +312 -0
  160. package/neural-interface/public/variant/2d/settings-gfx.js +98 -0
  161. package/neural-interface/public/variant/3d/background.js +884 -0
  162. package/neural-interface/public/variant/3d/camera.js +661 -0
  163. package/neural-interface/public/variant/3d/gfx.js +116 -0
  164. package/neural-interface/public/variant/3d/graph.js +2399 -0
  165. package/neural-interface/public/variant/3d/main.js +500 -0
  166. package/neural-interface/public/variant/3d/settings-gfx.js +228 -0
  167. package/neural-interface/scripts/rebuild-pty.js +100 -0
  168. package/neural-interface/server.js +15000 -0
  169. package/neural-interface/templates/CLAUDE-template.md +211 -0
  170. package/package.json +53 -0
  171. package/postinstall.js +135 -0
  172. package/setup.js +260 -0
  173. package/skills/leonardo/SKILL.md +159 -0
  174. package/skills/leonardo/icon.svg +1 -0
  175. package/skills/leonardo/modules/browser-guide.md +378 -0
  176. package/skills/leonardo/modules/image-prompter.md +277 -0
  177. package/skills/leonardo/modules/model-advisor.md +100 -0
  178. package/skills/leonardo/modules/prompt-library.md +110 -0
  179. package/skills/leonardo/modules/style-guide.md +186 -0
  180. package/skills/leonardo/modules/video-prompter.md +311 -0
  181. package/skills/synabun/SKILL.md +185 -0
  182. package/skills/synabun/icon.png +0 -0
  183. package/skills/synabun/modules/audit.md +374 -0
  184. package/skills/synabun/modules/changelog.md +194 -0
  185. package/skills/synabun/modules/idea.md +144 -0
  186. package/skills/synabun/modules/memorize.md +188 -0
  187. package/skills/synabun/synabunicon.png +0 -0
@@ -0,0 +1,228 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — 3D Graphics Settings Tab
3
+ // Self-registering settings tab for quality presets,
4
+ // floor styles, and fine-tuning controls
5
+ // ═══════════════════════════════════════════
6
+
7
+ import { registerSettingsTab } from '../../shared/registry.js';
8
+ import { state, emit, on } from '../../shared/state.js';
9
+ import { storage } from '../../shared/storage.js';
10
+ import {
11
+ gfx,
12
+ GFX_DEFAULTS,
13
+ GFX_PRESETS,
14
+ saveGfxConfig,
15
+ applyGfxPreset,
16
+ getActivePreset,
17
+ } from './gfx.js';
18
+
19
+
20
+ // ── Lazy bindings for functions still in the monolith ──
21
+ let _applyBgTheme = null;
22
+ let _applyFloorStyle = null;
23
+ let _getGraph = null;
24
+ let _setBloomEnabled = null;
25
+
26
+ export function setGraphicsHooks(hooks) {
27
+ if (hooks.applyBgTheme) _applyBgTheme = hooks.applyBgTheme;
28
+ if (hooks.applyFloorStyle) _applyFloorStyle = hooks.applyFloorStyle;
29
+ if (hooks.getGraph) _getGraph = hooks.getGraph;
30
+ if (hooks.setBloomEnabled) _setBloomEnabled = hooks.setBloomEnabled;
31
+ }
32
+
33
+
34
+ // ── Helper: range slider row ──
35
+
36
+ function rangeRow(key, label, min, max, step) {
37
+ const val = gfx[key];
38
+ const decimals = step < 1 ? 2 : 0;
39
+ return `<div class="gfx-row">
40
+ <span class="gfx-label">${label}</span>
41
+ <input type="range" data-key="${key}" min="${min}" max="${max}" step="${step}" value="${val}">
42
+ <span class="gfx-val" data-val="${key}">${Number(val).toFixed(decimals)}</span>
43
+ </div>`;
44
+ }
45
+
46
+
47
+ // ═══════════════════════════════════════════
48
+ // BUILD — returns the HTML string for the Graphics tab body
49
+ // ═══════════════════════════════════════════
50
+
51
+ function buildGraphicsTab() {
52
+ // ── Quality preset cards ──
53
+ const presetCards = Object.entries(GFX_PRESETS).map(([key, p]) => `
54
+ <div class="gfx-preset-card${getActivePreset() === key ? ' active' : ''}" data-preset="${key}">
55
+ <span class="gfx-preset-name">${p.label}</span>
56
+ <span class="gfx-preset-desc">${p.desc}</span>
57
+ </div>
58
+ `).join('');
59
+
60
+ // ── Floor style cards ──
61
+ const floorStyles = [
62
+ { key: 'grid', name: 'Grid', desc: 'Soft glowing lines' },
63
+ { key: 'dots', name: 'Dot Field', desc: 'Proximity glow dots' },
64
+ { key: 'hex', name: 'Hex Grid', desc: 'Hexagonal cells' },
65
+ { key: 'ripples', name: 'Ripples', desc: 'Concentric waves' },
66
+ { key: 'fog', name: 'Ground Fog', desc: 'Drifting mist' },
67
+ { key: 'none', name: 'None', desc: 'No floor effect' },
68
+ ];
69
+ const floorCards = floorStyles.map(f => `
70
+ <div class="gfx-preset-card floor-card${gfx.floorStyle === f.key ? ' active' : ''}" data-floor-style="${f.key}">
71
+ <span class="gfx-preset-name">${f.name}</span>
72
+ <span class="gfx-preset-desc">${f.desc}</span>
73
+ </div>
74
+ `).join('');
75
+
76
+ return `
77
+ <div class="gfx-presets" id="gfx-preset-cards" style="margin-bottom:14px">
78
+ ${presetCards}
79
+ </div>
80
+
81
+ <div class="iface-section">
82
+ <div class="gfx-group-title">Floor Effect</div>
83
+ <div class="gfx-presets gfx-floor-grid" id="gfx-floor-cards">
84
+ ${floorCards}
85
+ </div>
86
+ </div>
87
+
88
+ <div class="iface-section">
89
+ <div class="gfx-group-title">Links</div>
90
+ ${rangeRow('linkOpacity', 'Opacity', 0, 0.5, 0.01)}
91
+ </div>
92
+
93
+ <div class="iface-grid">
94
+ <div class="iface-section">
95
+ <div class="gfx-group-title">Camera</div>
96
+ ${rangeRow('clickZoom', 'Click Zoom', 40, 300, 10)}
97
+ ${rangeRow('gentleZoom', 'Gentle Zoom', 80, 400, 10)}
98
+ </div>
99
+ <div class="iface-section">
100
+ <div class="gfx-group-title">Background</div>
101
+ ${rangeRow('bgIntensity', 'Particle Glow', 0, 2, 0.05)}
102
+ ${rangeRow('bgParticleCount', 'Particle Count', 500, 5000, 100)}
103
+ </div>
104
+ </div>
105
+
106
+ <div class="iface-reset-wrap">
107
+ <button class="gfx-reset-btn" id="gfx-reset">Reset All to Defaults</button>
108
+ </div>
109
+ `;
110
+ }
111
+
112
+
113
+ // ═══════════════════════════════════════════
114
+ // INIT — wire up event handlers after HTML is injected
115
+ // ═══════════════════════════════════════════
116
+
117
+ function initGraphicsTab(container) {
118
+ // ── Preset card clicks ──
119
+ container.querySelectorAll('.gfx-preset-card[data-preset]').forEach(card => {
120
+ card.addEventListener('click', () => {
121
+ const g = _getGraph ? _getGraph() : null;
122
+ applyGfxPreset(card.dataset.preset, gfx, g, _applyBgTheme);
123
+
124
+ // Toggle bloom pass based on preset
125
+ if (_setBloomEnabled) _setBloomEnabled(gfx.bloomEnabled !== false);
126
+
127
+ container.querySelectorAll('.gfx-preset-card[data-preset]').forEach(c => {
128
+ c.classList.toggle('active', c.dataset.preset === card.dataset.preset);
129
+ });
130
+ container.querySelectorAll('.floor-card[data-floor-style]').forEach(c => {
131
+ c.classList.toggle('active', c.dataset.floorStyle === gfx.floorStyle);
132
+ });
133
+
134
+ // Sync slider values
135
+ container.querySelectorAll('input[type="range"][data-key]').forEach(input => {
136
+ const key = input.dataset.key;
137
+ if (gfx[key] !== undefined) {
138
+ input.value = gfx[key];
139
+ const step = parseFloat(input.step);
140
+ const valSpan = container.querySelector(`[data-val="${key}"]`);
141
+ if (valSpan) valSpan.textContent = Number(gfx[key]).toFixed(step < 1 ? 2 : 0);
142
+ }
143
+ });
144
+ });
145
+ });
146
+
147
+ // ── Floor style card clicks ──
148
+ container.querySelectorAll('.floor-card[data-floor-style]').forEach(card => {
149
+ card.addEventListener('click', () => {
150
+ const style = card.dataset.floorStyle;
151
+ gfx.floorStyle = style;
152
+ saveGfxConfig(gfx);
153
+
154
+ container.querySelectorAll('.floor-card').forEach(c => c.classList.remove('active'));
155
+ card.classList.add('active');
156
+
157
+ if (_applyFloorStyle) _applyFloorStyle(style);
158
+ });
159
+ });
160
+
161
+ // ── Live range slider updates ──
162
+ container.querySelectorAll('input[type="range"][data-key]').forEach(input => {
163
+ input.addEventListener('input', () => {
164
+ const key = input.dataset.key;
165
+ const val = parseFloat(input.value);
166
+ gfx[key] = val;
167
+
168
+ const step = parseFloat(input.step);
169
+ const valSpan = container.querySelector(`[data-val="${key}"]`);
170
+ if (valSpan) valSpan.textContent = val.toFixed(step < 1 ? 2 : 0);
171
+
172
+ saveGfxConfig(gfx);
173
+
174
+ // Clear preset selection when manually tweaking
175
+ storage.removeItem('neural-gfx-preset');
176
+ container.querySelectorAll('.gfx-preset-card[data-preset]').forEach(c => c.classList.remove('active'));
177
+
178
+ // Live-apply visual changes
179
+ if (key === 'bgParticleCount' && _applyBgTheme) {
180
+ _applyBgTheme(gfx.bgTheme || 'deep-grid');
181
+ }
182
+ });
183
+ });
184
+
185
+ // ── Reset to defaults ──
186
+ const resetBtn = container.querySelector('#gfx-reset');
187
+ if (resetBtn) {
188
+ resetBtn.addEventListener('click', () => {
189
+ Object.assign(gfx, GFX_DEFAULTS);
190
+ storage.removeItem('neural-gfx-config');
191
+ storage.removeItem('neural-gfx-preset');
192
+
193
+ container.querySelectorAll('input[type="range"][data-key]').forEach(input => {
194
+ const key = input.dataset.key;
195
+ if (GFX_DEFAULTS[key] == null) return;
196
+ input.value = GFX_DEFAULTS[key];
197
+ const step = parseFloat(input.step);
198
+ const valSpan = container.querySelector(`[data-val="${key}"]`);
199
+ if (valSpan) valSpan.textContent = Number(GFX_DEFAULTS[key]).toFixed(step < 1 ? 2 : 0);
200
+ });
201
+
202
+ container.querySelectorAll('.gfx-preset-card[data-preset]').forEach(c => c.classList.remove('active'));
203
+ container.querySelectorAll('.floor-card[data-floor-style]').forEach(c => {
204
+ c.classList.toggle('active', c.dataset.floorStyle === GFX_DEFAULTS.floorStyle);
205
+ });
206
+
207
+ const g = _getGraph ? _getGraph() : null;
208
+ if (g) {
209
+ if (_applyBgTheme) _applyBgTheme(GFX_DEFAULTS.bgTheme || 'deep-grid');
210
+ if (_applyFloorStyle) _applyFloorStyle(GFX_DEFAULTS.floorStyle || 'grid');
211
+ }
212
+ });
213
+ }
214
+ }
215
+
216
+
217
+ // ═══════════════════════════════════════════
218
+ // SELF-REGISTER — side effect on import
219
+ // ═══════════════════════════════════════════
220
+
221
+ registerSettingsTab({
222
+ id: 'graphics',
223
+ label: 'Graphics',
224
+ icon: '🎨',
225
+ order: 70,
226
+ build: buildGraphicsTab,
227
+ afterRender: initGraphicsTab,
228
+ });
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Rebuild node-pty native addon for the current platform + Node.js ABI.
5
+ *
6
+ * Runs automatically via postinstall. If the prebuilt binary already works,
7
+ * this is a no-op. If it doesn't (ABI mismatch, missing prebuild), we
8
+ * rebuild from source using node-gyp.
9
+ *
10
+ * Works on macOS (ARM + Intel), Linux (x64/ARM), and Windows (x64/ARM).
11
+ */
12
+
13
+ import { execSync } from 'node:child_process';
14
+ import { existsSync, cpSync, mkdirSync, chmodSync } from 'node:fs';
15
+ import { resolve, dirname } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const __dirname = dirname(fileURLToPath(import.meta.url));
19
+ const ptyDir = resolve(__dirname, '..', 'node_modules', 'node-pty');
20
+
21
+ if (!existsSync(ptyDir)) {
22
+ console.log('[rebuild-pty] node-pty not found, skipping');
23
+ process.exit(0);
24
+ }
25
+
26
+ // Step 1a: Fix spawn-helper permissions on Unix (cpSync/npm don't preserve +x)
27
+ if (process.platform !== 'win32') {
28
+ for (const dir of ['prebuilds/darwin-arm64', 'prebuilds/darwin-x64', 'prebuilds/linux-x64', 'prebuilds/linux-arm64', 'build/Release']) {
29
+ const sh = resolve(ptyDir, dir, 'spawn-helper');
30
+ if (existsSync(sh)) try { chmodSync(sh, 0o755); } catch {}
31
+ }
32
+
33
+ // Fix claude CLI binary permissions (same npm +x issue)
34
+ const claudeBin = resolve(__dirname, '..', 'node_modules', '.bin', 'claude');
35
+ if (existsSync(claudeBin)) try { chmodSync(claudeBin, 0o755); } catch {}
36
+ const claudeCli = resolve(__dirname, '..', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
37
+ if (existsSync(claudeCli)) try { chmodSync(claudeCli, 0o755); } catch {}
38
+ }
39
+
40
+ // Step 1b: Check if node-pty actually works (spawn test, not just module load)
41
+ try {
42
+ const shell = process.platform === 'win32' ? 'cmd.exe' : '/bin/sh';
43
+ const testCode = [
44
+ `const p=require(${JSON.stringify(ptyDir)})`,
45
+ `const t=p.spawn(${JSON.stringify(shell)},[],{cols:10,rows:10})`,
46
+ `setTimeout(()=>{t.kill();process.exit(0)},500)`,
47
+ ].join(';');
48
+ execSync(`node -e ${JSON.stringify(testCode)}`, { stdio: 'pipe', timeout: 10_000 });
49
+ console.log('[rebuild-pty] node-pty prebuilt binary works, skipping rebuild');
50
+ process.exit(0);
51
+ } catch {
52
+ console.log('[rebuild-pty] Prebuilt binary incompatible or spawn failed, rebuilding from source...');
53
+ }
54
+
55
+ // Step 2: Rebuild from source
56
+ try {
57
+ execSync('npx node-gyp rebuild', {
58
+ cwd: ptyDir,
59
+ stdio: 'inherit',
60
+ timeout: 120_000,
61
+ });
62
+ } catch (err) {
63
+ console.error('[rebuild-pty] node-gyp rebuild failed:', err.message);
64
+ console.error('[rebuild-pty] Terminal features may not work. Ensure build tools are installed:');
65
+ console.error(' macOS: xcode-select --install');
66
+ console.error(' Linux: sudo apt install build-essential python3');
67
+ console.error(' Windows: Install Visual Studio Build Tools 2022 with "Desktop development with C++" workload');
68
+ console.error(' Or: winget install Microsoft.VisualStudio.2022.BuildTools --override "--add Microsoft.VisualStudio.Workload.VCTools"');
69
+ process.exit(0); // non-fatal — the rest of the app can still run
70
+ }
71
+
72
+ // Step 3: Copy rebuilt binaries into prebuilds/ so node-pty finds them
73
+ const arch = process.arch;
74
+ const platform = process.platform;
75
+ const prebuildsDir = resolve(ptyDir, 'prebuilds', `${platform}-${arch}`);
76
+ const buildDir = resolve(ptyDir, 'build', 'Release');
77
+
78
+ if (existsSync(buildDir)) {
79
+ mkdirSync(prebuildsDir, { recursive: true });
80
+
81
+ const files = ['pty.node', 'spawn-helper', 'conpty.node', 'conpty_console_list.node', 'winpty-agent.exe', 'winpty.dll'];
82
+ for (const file of files) {
83
+ const src = resolve(buildDir, file);
84
+ if (existsSync(src)) {
85
+ cpSync(src, resolve(prebuildsDir, file));
86
+ }
87
+ }
88
+
89
+ // Ensure spawn-helper is executable (cpSync doesn't preserve execute bit)
90
+ if (process.platform !== 'win32') {
91
+ const spawnHelper = resolve(prebuildsDir, 'spawn-helper');
92
+ if (existsSync(spawnHelper)) {
93
+ chmodSync(spawnHelper, 0o755);
94
+ }
95
+ }
96
+
97
+ console.log(`[rebuild-pty] Rebuilt for ${platform}-${arch} (Node ${process.version})`);
98
+ } else {
99
+ console.warn('[rebuild-pty] build/Release not found after rebuild');
100
+ }