@aion0/forge 0.4.11 → 0.4.13

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
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="app/icon.png" width="80" height="80" alt="Forge">
2
+ <img src="app/icon.svg" width="80" height="80" alt="Forge">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">Forge</h1>
package/RELEASE_NOTES.md CHANGED
@@ -1,13 +1,11 @@
1
- # Forge v0.4.11
1
+ # Forge v0.4.13
2
2
 
3
3
  Released: 2026-03-23
4
4
 
5
- ## Changes since v0.4.10
5
+ ## Changes since v0.4.12
6
6
 
7
- ### Other
8
- - mobile page
9
- - init version for mobile page
10
- - change sync period time
7
+ ### Bug Fixes
8
+ - fix: bell notification only triggers on claude completion, not terminal idle
11
9
 
12
10
 
13
- **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.4.10...v0.4.11
11
+ **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.4.12...v0.4.13
@@ -23,7 +23,8 @@ const CODE_EXTS = new Set([
23
23
  '.css', '.scss', '.html', '.json', '.yaml', '.yml', '.toml',
24
24
  '.md', '.txt', '.sh', '.bash', '.zsh', '.fish',
25
25
  '.sql', '.graphql', '.proto', '.env', '.gitignore',
26
- '.xml', '.csv', '.lock',
26
+ '.xml', '.csv', '.lock', '.properties', '.gradle', '.groovy',
27
+ '.scala', '.clj', '.cljs', '.jsp', '.erb', '.vue', '.svelte',
27
28
  ]);
28
29
 
29
30
  const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.bmp', '.ico', '.avif']);
@@ -36,7 +37,7 @@ function isCodeFile(name: string): boolean {
36
37
  }
37
38
 
38
39
  function scanDir(dir: string, base: string, depth: number = 0): FileNode[] {
39
- if (depth > 5) return [];
40
+ if (depth > 10) return [];
40
41
  try {
41
42
  const entries = readdirSync(dir, { withFileTypes: true });
42
43
  const nodes: FileNode[] = [];
@@ -0,0 +1,35 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { loadSettings } from '@/lib/settings';
3
+ import { addNotification } from '@/lib/notifications';
4
+
5
+ export async function POST(req: Request) {
6
+ const { tabLabel } = await req.json();
7
+
8
+ const label = tabLabel || 'Terminal';
9
+
10
+ // In-app notification
11
+ try {
12
+ addNotification('terminal_bell', `Terminal idle: ${label}`, `Claude appears to have finished in "${label}".`);
13
+ } catch {}
14
+
15
+ // Telegram notification
16
+ const settings = loadSettings();
17
+ const { telegramBotToken, telegramChatId } = settings;
18
+ if (telegramBotToken && telegramChatId) {
19
+ try {
20
+ const chatIds = String(telegramChatId).split(',').map(s => s.trim()).filter(Boolean);
21
+ for (const chatId of chatIds) {
22
+ await fetch(`https://api.telegram.org/bot${telegramBotToken}/sendMessage`, {
23
+ method: 'POST',
24
+ headers: { 'Content-Type': 'application/json' },
25
+ body: JSON.stringify({
26
+ chat_id: chatId,
27
+ text: `🔔 Forge — Terminal idle\n\n"${label}" appears to have finished.`,
28
+ }),
29
+ });
30
+ }
31
+ } catch {}
32
+ }
33
+
34
+ return NextResponse.json({ ok: true });
35
+ }
package/app/icon.svg ADDED
@@ -0,0 +1,106 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
3
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
4
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg"
5
+ width="1050.000000pt" height="1102.000000pt" viewBox="0 0 1050.000000 1102.000000"
6
+ preserveAspectRatio="xMidYMid meet">
7
+
8
+ <g transform="translate(0.000000,1102.000000) scale(0.100000,-0.100000)"
9
+ fill="#000000" stroke="none">
10
+ <path d="M0 5510 l0 -5510 5250 0 5250 0 0 5510 0 5510 -5250 0 -5250 0 0
11
+ -5510z m2410 3369 c37 -18 164 -120 395 -319 584 -503 1025 -883 1101 -946 39
12
+ -34 111 -96 159 -138 52 -45 96 -76 109 -76 12 0 233 45 492 99 258 55 503
13
+ 106 544 115 67 13 83 14 137 1 74 -17 147 -33 398 -85 105 -21 233 -48 285
14
+ -60 237 -52 327 -70 350 -70 25 0 127 84 655 541 61 52 162 139 225 193 63 54
15
+ 171 147 240 207 475 410 597 512 643 536 74 38 198 44 279 13 66 -24 145 -92
16
+ 181 -153 60 -101 57 -27 57 -1656 l0 -1492 54 -217 c57 -224 150 -590 185
17
+ -722 10 -41 38 -147 62 -235 23 -88 47 -185 54 -215 7 -30 16 -67 20 -82 6
18
+ -25 5 -28 -19 -28 -15 0 -29 6 -32 14 -4 11 -13 12 -35 6 -16 -5 -68 -12 -114
19
+ -15 l-84 -7 -6 29 c-4 15 -9 35 -12 43 -2 8 -29 110 -58 225 -30 116 -75 289
20
+ -100 385 -25 96 -81 316 -125 488 l-80 313 0 1495 c0 1664 5 1554 -65 1554
21
+ -29 0 -49 -13 -123 -77 -88 -77 -479 -412 -617 -528 -40 -33 -213 -181 -385
22
+ -330 -173 -148 -399 -343 -504 -432 -121 -105 -197 -163 -211 -163 -21 0 -115
23
+ 19 -525 106 -204 43 -302 64 -500 105 l-155 32 -115 -23 c-63 -12 -155 -31
24
+ -205 -42 -49 -11 -133 -28 -185 -39 -52 -11 -124 -27 -160 -34 -259 -56 -505
25
+ -105 -526 -105 -17 0 -66 36 -162 119 -341 296 -862 743 -1486 1275 -166 142
26
+ -194 156 -230 120 -9 -8 -17 -16 -18 -17 -2 -1 -5 -688 -8 -1527 l-5 -1525
27
+ -53 -195 c-28 -107 -79 -298 -111 -425 -33 -126 -72 -275 -86 -330 -15 -55
28
+ -46 -177 -70 -271 -64 -248 -50 -226 -142 -219 -129 10 -144 11 -149 3 -8 -13
29
+ -54 -9 -54 5 0 6 5 28 11 47 6 19 17 60 24 90 11 50 115 454 165 645 12 44 56
30
+ 213 98 375 l77 295 5 1540 5 1540 33 67 c40 81 117 155 193 186 78 31 202 27
31
+ 279 -9z m560 -1324 c0 -85 0 -85 -26 -85 -36 0 -81 -29 -94 -59 -6 -14 -14
32
+ -112 -19 -217 -9 -212 -21 -257 -83 -309 l-30 -25 40 -44 c58 -63 73 -120 78
33
+ -301 2 -82 4 -160 4 -173 0 -37 52 -82 94 -82 l36 0 0 -85 0 -85 -47 0 c-107
34
+ 1 -190 39 -237 111 -42 63 -56 137 -56 294 0 77 -3 157 -6 177 -9 50 -43 85
35
+ -98 100 l-46 12 0 78 c0 77 0 78 28 84 111 24 115 34 123 268 6 204 13 236 58
36
+ 307 45 72 133 113 254 118 l27 1 0 -85z m4810 42 c117 -58 150 -147 150 -406
37
+ 0 -192 17 -229 113 -246 l38 -7 -3 -76 -3 -75 -49 -17 c-84 -28 -89 -41 -97
38
+ -254 -4 -101 -10 -199 -14 -218 -17 -79 -79 -154 -153 -185 -19 -8 -66 -15
39
+ -106 -16 l-71 -2 0 80 0 79 45 8 c81 13 86 27 93 239 7 208 18 258 73 316 19
40
+ 20 34 37 34 38 0 1 -16 19 -35 40 -52 57 -65 109 -66 254 0 196 -10 259 -43
41
+ 287 -15 13 -45 27 -67 30 l-39 7 0 77 c0 54 4 80 13 83 32 12 126 -6 187 -36z
42
+ m-3638 -2314 c233 -236 490 -501 596 -615 72 -77 72 -77 72 -133 l0 -57 -177
43
+ -181 c-455 -467 -688 -697 -710 -697 -23 0 -198 181 -198 206 0 7 159 173 354
44
+ 369 195 195 353 357 350 360 -17 18 -186 194 -424 438 -154 159 -282 293 -284
45
+ 299 -3 10 197 218 210 218 3 0 98 -93 211 -207z m2473 -1633 l0 -145 -95 -6
46
+ c-121 -9 -1077 -8 -1320 0 l-185 6 -3 134 c-2 90 1 138 9 147 10 12 136 14
47
+ 803 12 l791 -3 0 -145z m-4912 -141 c21 -35 42 -73 48 -84 22 -47 71 -129 86
48
+ -143 14 -14 16 -14 25 1 5 9 14 17 20 17 7 0 6 -5 -2 -15 -6 -8 -9 -19 -5 -26
49
+ 4 -6 2 -15 -5 -19 -9 -5 -5 -20 13 -56 13 -27 28 -59 32 -72 4 -13 25 -55 47
50
+ -93 38 -65 44 -106 16 -97 -11 4 -40 53 -160 278 -66 123 -173 337 -188 374
51
+ -10 28 -10 29 12 14 13 -9 40 -44 61 -79z m7221 54 c-4 -16 -28 -68 -54 -118
52
+ -26 -49 -80 -153 -120 -230 -40 -77 -90 -170 -112 -207 -21 -36 -38 -71 -38
53
+ -77 0 -13 -27 -15 -34 -2 -3 5 8 41 24 79 17 39 30 78 30 87 0 9 5 13 11 9 8
54
+ -4 9 1 4 17 -4 13 -4 21 0 17 5 -4 14 0 21 10 9 13 10 20 1 31 -10 12 -9 14 4
55
+ 9 17 -7 40 30 132 212 74 146 149 240 131 163z m-284 -35 c-7 -18 -33 -75 -57
56
+ -126 -24 -51 -43 -99 -43 -106 0 -7 -7 -19 -15 -26 -9 -7 -13 -20 -10 -30 3
57
+ -10 -5 -26 -21 -41 -14 -13 -46 -59 -71 -101 -30 -53 -49 -75 -59 -71 -8 3
58
+ -14 9 -14 13 0 28 233 461 252 468 4 2 8 8 8 13 0 11 33 47 39 43 2 -2 -2 -18
59
+ -9 -36z m-6543 -260 c99 -190 119 -235 106 -241 -18 -11 -8 -24 -87 113 -87
60
+ 148 -108 179 -124 180 -14 0 -36 49 -27 58 3 4 8 -4 12 -16 3 -12 10 -22 15
61
+ -22 5 0 0 17 -11 38 -28 52 -51 111 -51 132 0 10 -5 20 -12 22 -7 3 -8 9 -3
62
+ 18 15 24 52 -34 182 -282z m-231 54 c-18 -18 -29 -12 -20 11 3 9 12 13 21 10
63
+ 13 -5 13 -8 -1 -21z m6779 -162 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2
64
+ 10 4 10 3 0 8 -4 11 -10z m-751 -568 c-22 -19 -66 -52 -99 -75 -33 -22 -99
65
+ -69 -146 -103 -48 -35 -89 -64 -93 -64 -3 0 -14 -7 -23 -16 -17 -14 -69 -51
66
+ -248 -174 -36 -25 -107 -74 -158 -110 -51 -36 -100 -69 -109 -75 -8 -5 -43
67
+ -30 -78 -55 -34 -25 -75 -53 -92 -62 -17 -9 -34 -21 -37 -25 -15 -20 -72 -53
68
+ -82 -47 -12 7 48 63 69 64 6 0 12 4 12 8 0 5 33 31 72 58 40 27 77 53 83 57
69
+ 16 12 252 181 414 295 73 52 180 126 205 142 27 17 67 46 204 147 62 45 106
70
+ 71 121 70 22 -1 21 -4 -15 -35z m-5149 -18 c5 -5 30 -23 54 -39 25 -15 50 -32
71
+ 55 -37 6 -5 65 -47 131 -93 66 -46 125 -88 130 -92 6 -4 30 -21 55 -37 25 -16
72
+ 72 -50 105 -75 58 -45 100 -75 134 -96 9 -5 32 -22 51 -37 20 -14 71 -50 115
73
+ -79 44 -29 97 -71 119 -93 21 -21 36 -34 32 -28 -12 20 10 13 36 -10 14 -13
74
+ 35 -30 47 -38 11 -8 21 -20 21 -28 0 -12 -27 2 -75 40 -5 5 -53 37 -105 71
75
+ -134 89 -261 177 -285 197 -11 10 -74 54 -140 99 -66 45 -147 102 -180 126
76
+ -70 52 -161 116 -183 130 -12 7 -140 100 -181 131 -1 1 0 7 3 13 7 11 39 -2
77
+ 61 -25z m5388 -81 c-5 -12 -3 -14 8 -8 68 39 -178 -142 -435 -320 -154 -106
78
+ -291 -203 -384 -270 -53 -39 -135 -96 -182 -127 -102 -69 -351 -246 -383 -273
79
+ -52 -44 -168 -104 -167 -88 1 11 75 67 195 148 96 65 153 106 288 206 34 25
80
+ 107 76 162 114 93 63 384 273 467 337 20 15 39 28 43 28 3 0 45 29 93 64 78
81
+ 57 294 206 299 206 1 0 -1 -7 -4 -17z m-5615 -47 c71 -47 139 -94 159 -111 6
82
+ -5 49 -36 95 -68 46 -32 162 -114 258 -183 96 -69 231 -164 300 -211 122 -85
83
+ 260 -185 290 -210 8 -7 24 -19 35 -27 11 -8 17 -17 13 -21 -7 -8 -42 10 -84
84
+ 42 -16 12 -76 54 -134 93 -58 39 -143 98 -190 131 -47 33 -96 68 -110 77 -26
85
+ 17 -177 125 -195 139 -5 4 -30 21 -55 38 -25 16 -95 66 -156 111 -61 45 -122
86
+ 87 -135 94 -25 14 -113 79 -140 105 -9 8 -20 15 -26 15 -5 0 -10 7 -10 15 0
87
+ 23 13 19 85 -29z m1347 -667 c11 -17 -1 -21 -15 -4 -8 9 -8 15 -2 15 6 0 14
88
+ -5 17 -11z m2850 1 c10 -16 -258 -206 -276 -195 -15 10 0 25 19 18 9 -3 14 -3
89
+ 10 2 -9 9 26 42 53 50 11 4 19 11 19 17 0 6 3 8 7 5 3 -4 16 5 29 18 13 14 27
90
+ 25 31 26 4 0 28 15 53 34 53 40 47 37 55 25z m-2795 -49 c-1 -15 -2 -15 -13 0
91
+ -7 9 -19 14 -27 12 -13 -4 -13 -3 -1 5 19 13 41 4 41 -17z m178 -117 c34 -27
92
+ 37 -39 8 -28 -9 3 -14 10 -11 14 3 4 -8 10 -23 13 -15 4 -40 19 -57 34 -16 15
93
+ -43 35 -58 43 -16 8 -24 16 -18 18 6 2 8 8 5 14 -7 11 99 -63 154 -108z m-190
94
+ -152 c62 -43 76 -62 47 -62 -8 0 -15 3 -15 8 0 4 -15 13 -32 21 -18 8 -35 17
95
+ -38 20 -3 3 -27 22 -55 40 -27 19 -52 37 -55 41 -3 3 -24 18 -47 33 -25 18
96
+ -40 34 -36 43 5 13 36 -6 231 -144z m452 28 c0 -5 -15 -10 -34 -10 -19 0 -38
97
+ 5 -41 10 -4 6 10 10 34 10 23 0 41 -4 41 -10z m685 5 c-25 -8 -675 -12 -675
98
+ -4 0 5 141 9 343 8 188 0 338 -2 332 -4z m705 -5 c-96 -3 -249 -3 -340 0 -113
99
+ 4 -58 6 175 6 246 0 291 -2 165 -6z m263 3 c-7 -2 -21 -2 -30 0 -10 3 -4 5 12
100
+ 5 17 0 24 -2 18 -5z m165 0 c-21 -2 -57 -2 -80 0 -24 2 -7 4 37 4 44 0 63 -2
101
+ 43 -4z m401 -119 c-6 -7 -15 -12 -19 -9 -5 3 -11 1 -15 -5 -4 -6 -11 -7 -17
102
+ -4 -5 4 2 14 18 25 30 20 52 15 33 -7z m-134 -84 c3 -5 -3 -10 -15 -10 -12 0
103
+ -18 5 -15 10 3 6 10 10 15 10 5 0 12 -4 15 -10z m-71 -39 c3 -5 -11 -7 -32 -4
104
+ -49 7 -54 13 -9 13 20 0 38 -4 41 -9z"/>
105
+ </g>
106
+ </svg>
@@ -112,6 +112,10 @@ const KEYWORDS = new Set([
112
112
  'true', 'false', 'null', 'undefined', 'void',
113
113
  'def', 'self', 'None', 'True', 'False', 'class', 'lambda', 'with', 'as', 'in', 'not', 'and', 'or',
114
114
  'func', 'package', 'struct', 'go', 'defer', 'select', 'chan', 'map', 'range',
115
+ 'final', 'synchronized', 'volatile', 'transient', 'native',
116
+ 'throws', 'int', 'long', 'double', 'float', 'char', 'byte', 'short', 'boolean',
117
+ 'override',
118
+ 'val', 'object', 'trait', 'sealed', 'implicit', 'lazy', 'match',
115
119
  ]);
116
120
 
117
121
  function highlightLine(line: string, lang: string): React.ReactNode {
@@ -13,6 +13,10 @@ const KEYWORDS = new Set([
13
13
  'public', 'private', 'protected', 'static', 'abstract',
14
14
  'true', 'false', 'null', 'undefined', 'void',
15
15
  'def', 'self', 'None', 'True', 'False', 'lambda', 'with', 'as', 'in', 'not', 'and', 'or',
16
+ 'package', 'final', 'synchronized', 'volatile', 'transient', 'native',
17
+ 'throws', 'int', 'long', 'double', 'float', 'char', 'byte', 'short', 'boolean',
18
+ 'override', 'struct', 'func', 'go', 'defer', 'select', 'chan', 'range',
19
+ 'val', 'var', 'def', 'object', 'trait', 'sealed', 'implicit', 'lazy', 'match',
16
20
  ]);
17
21
 
18
22
  function highlightLine(line: string): React.ReactNode {
@@ -37,6 +37,7 @@ interface TabState {
37
37
  ratios: Record<number, number>;
38
38
  activeId: number;
39
39
  projectPath?: string;
40
+ bellEnabled?: boolean;
40
41
  }
41
42
 
42
43
  // ─── Layout persistence ──────────────────────────────────────
@@ -145,6 +146,11 @@ function firstTerminalId(n: SplitNode): number {
145
146
  return n.type === 'terminal' ? n.id : firstTerminalId(n.first);
146
147
  }
147
148
 
149
+ function collectPaneIds(tree: SplitNode): number[] {
150
+ if (tree.type === 'terminal') return [tree.id];
151
+ return [...collectPaneIds(tree.first), ...collectPaneIds(tree.second)];
152
+ }
153
+
148
154
  function collectSessionNames(tree: SplitNode): string[] {
149
155
  if (tree.type === 'terminal') return tree.sessionName ? [tree.sessionName] : [];
150
156
  return [...collectSessionNames(tree.first), ...collectSessionNames(tree.second)];
@@ -158,6 +164,34 @@ function collectAllSessionNames(tabs: TabState[]): string[] {
158
164
 
159
165
  const pendingCommands = new Map<number, string>();
160
166
 
167
+ // ─── Bell notification tracking ─────────────────────────────
168
+
169
+ const bellEnabledPanes = new Set<number>();
170
+ const bellPaneLabels = new Map<number, string>();
171
+ const bellLastFired = new Map<number, number>(); // paneId -> timestamp
172
+ const BELL_COOLDOWN = 30000; // 30s cooldown between bells
173
+
174
+ function fireBellNotification(paneId: number) {
175
+ const now = Date.now();
176
+ const last = bellLastFired.get(paneId) || 0;
177
+ if (now - last < BELL_COOLDOWN) return;
178
+ bellLastFired.set(paneId, now);
179
+
180
+ const label = bellPaneLabels.get(paneId) || 'Terminal';
181
+
182
+ // Browser notification
183
+ if (typeof Notification !== 'undefined' && Notification.permission === 'granted') {
184
+ new Notification('Forge — Terminal Idle', { body: `"${label}" appears to have finished.`, icon: '/icon.png' });
185
+ }
186
+
187
+ // Telegram + in-app via API
188
+ fetch('/api/terminal-bell', {
189
+ method: 'POST',
190
+ headers: { 'Content-Type': 'application/json' },
191
+ body: JSON.stringify({ tabLabel: label }),
192
+ }).catch(() => {});
193
+ }
194
+
161
195
  // ─── Global drag lock — suppress terminal fit() during split drag ──
162
196
 
163
197
  let globalDragging = false;
@@ -498,6 +532,29 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
498
532
 
499
533
  const usedSessions = collectAllSessionNames(tabs);
500
534
 
535
+ // Toggle bell for a tab
536
+ const toggleBell = useCallback((tabId: number) => {
537
+ if (typeof Notification !== 'undefined' && Notification.permission === 'default') {
538
+ Notification.requestPermission();
539
+ }
540
+ setTabs(prev => prev.map(t => t.id === tabId ? { ...t, bellEnabled: !t.bellEnabled } : t));
541
+ }, []);
542
+
543
+ // Sync bell state to module-level sets for MemoTerminalPane to read
544
+ useEffect(() => {
545
+ bellEnabledPanes.clear();
546
+ bellPaneLabels.clear();
547
+ for (const tab of tabs) {
548
+ if (tab.bellEnabled) {
549
+ const paneIds = collectPaneIds(tab.tree);
550
+ for (const id of paneIds) {
551
+ bellEnabledPanes.add(id);
552
+ bellPaneLabels.set(id, tab.label);
553
+ }
554
+ }
555
+ }
556
+ }, [tabs]);
557
+
501
558
  // Auto-refresh tmux sessions periodically to show detached count
502
559
  useEffect(() => {
503
560
  if (!hydrated) return;
@@ -570,6 +627,11 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
570
627
  {tab.label}
571
628
  </span>
572
629
  )}
630
+ <button
631
+ onClick={(e) => { e.stopPropagation(); toggleBell(tab.id); }}
632
+ className={`text-[10px] ml-1 ${tab.bellEnabled ? 'text-yellow-400' : 'text-gray-600 hover:text-gray-400'}`}
633
+ title={tab.bellEnabled ? 'Disable notification' : 'Enable notification when idle'}
634
+ >{tab.bellEnabled ? '🔔' : '🔕'}</button>
573
635
  {tabs.length > 1 && (
574
636
  <button
575
637
  onClick={(e) => { e.stopPropagation(); closeTab(tab.id); }}
@@ -1058,6 +1120,7 @@ const MemoTerminalPane = memo(function TerminalPane({
1058
1120
  if (!containerRef.current) return;
1059
1121
 
1060
1122
  let disposed = false; // guard against post-cleanup writes (React Strict Mode)
1123
+ let bellOutputBuffer = '';
1061
1124
 
1062
1125
  // Read terminal theme from CSS variables
1063
1126
  const cs = getComputedStyle(document.documentElement);
@@ -1194,6 +1257,17 @@ const MemoTerminalPane = memo(function TerminalPane({
1194
1257
  const msg = JSON.parse(event.data);
1195
1258
  if (msg.type === 'output') {
1196
1259
  try { term.write(msg.data); } catch {};
1260
+ // Bell: detect claude completion (cost summary or prompt after activity)
1261
+ if (bellEnabledPanes.has(id)) {
1262
+ bellOutputBuffer += msg.data as string;
1263
+ // Keep only last 500 chars
1264
+ if (bellOutputBuffer.length > 500) bellOutputBuffer = bellOutputBuffer.slice(-500);
1265
+ // Claude outputs cost/token info when done, or returns to prompt ❯
1266
+ if (bellOutputBuffer.includes('input tokens') || bellOutputBuffer.includes('output tokens') || bellOutputBuffer.includes('api_cost')) {
1267
+ bellOutputBuffer = '';
1268
+ fireBellNotification(id);
1269
+ }
1270
+ }
1197
1271
  } else if (msg.type === 'connected') {
1198
1272
  connectedSession = msg.sessionName;
1199
1273
  createRetries = 0;
package/next-env.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /// <reference types="next" />
2
2
  /// <reference types="next/image-types/global" />
3
- import "./.next/types/routes.d.ts";
3
+ import "./.next/dev/types/routes.d.ts";
4
4
 
5
5
  // NOTE: This file should not be edited
6
6
  // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.4.11",
3
+ "version": "0.4.13",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {