@kenkaiiii/gg-boss 4.3.140 → 4.3.142
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/dist/{chunk-JVQDTPYR.js → chunk-3EWLK53W.js} +18 -9
- package/dist/{chunk-JVQDTPYR.js.map → chunk-3EWLK53W.js.map} +1 -1
- package/dist/{chunk-5WNYQQPQ.js → chunk-QT366Y52.js} +4 -3
- package/dist/{chunk-5WNYQQPQ.js.map → chunk-QT366Y52.js.map} +1 -1
- package/dist/{chunk-PQAHDHVY.js → chunk-WJ4S4TOY.js} +3 -2
- package/dist/{chunk-PQAHDHVY.js.map → chunk-WJ4S4TOY.js.map} +1 -1
- package/dist/{chunk-B2WQ5E5J.js → chunk-YNWFCUMR.js} +2 -1
- package/dist/{chunk-B2WQ5E5J.js.map → chunk-YNWFCUMR.js.map} +1 -1
- package/dist/cli.js +2248 -182
- package/dist/cli.js.map +1 -1
- package/dist/{devtools-VBUDNGEI.js → devtools-4TI4D7F2.js} +3 -2
- package/dist/{devtools-VBUDNGEI.js.map → devtools-4TI4D7F2.js.map} +1 -1
- package/dist/{dist-7DAPKZGX.js → dist-VXOVSHZ5.js} +3 -2
- package/dist/{dist-7DAPKZGX.js.map → dist-VXOVSHZ5.js.map} +1 -1
- package/dist/{ignore-3AEIALHQ.js → ignore-76P4EAAU.js} +3 -2
- package/dist/{ignore-3AEIALHQ.js.map → ignore-76P4EAAU.js.map} +1 -1
- package/dist/index.js +21 -4
- package/dist/index.js.map +1 -1
- package/dist/{out-D65DTPFZ.js → out-XEXARMKS.js} +3 -2
- package/dist/{out-D65DTPFZ.js.map → out-XEXARMKS.js.map} +1 -1
- package/dist/pixel-WPYTQADG.js +14 -0
- package/dist/{pixel-fix-ALWXCLTS.js → pixel-fix-4WGZAJ5W.js} +4 -3
- package/dist/{pixel-fix-ALWXCLTS.js.map → pixel-fix-4WGZAJ5W.js.map} +1 -1
- package/package.json +10 -11
- package/dist/audio.d.ts +0 -21
- package/dist/audio.d.ts.map +0 -1
- package/dist/audio.js +0 -231
- package/dist/audio.js.map +0 -1
- package/dist/audio.test.d.ts +0 -2
- package/dist/audio.test.d.ts.map +0 -1
- package/dist/audio.test.js +0 -13
- package/dist/audio.test.js.map +0 -1
- package/dist/auto-update.d.ts +0 -24
- package/dist/auto-update.d.ts.map +0 -1
- package/dist/auto-update.js +0 -231
- package/dist/auto-update.js.map +0 -1
- package/dist/banner.d.ts +0 -17
- package/dist/banner.d.ts.map +0 -1
- package/dist/banner.js +0 -25
- package/dist/banner.js.map +0 -1
- package/dist/boss-footer.d.ts +0 -25
- package/dist/boss-footer.d.ts.map +0 -1
- package/dist/boss-footer.js +0 -107
- package/dist/boss-footer.js.map +0 -1
- package/dist/boss-phrases.d.ts +0 -9
- package/dist/boss-phrases.d.ts.map +0 -1
- package/dist/boss-phrases.js +0 -71
- package/dist/boss-phrases.js.map +0 -1
- package/dist/boss-store.d.ts +0 -245
- package/dist/boss-store.d.ts.map +0 -1
- package/dist/boss-store.js +0 -623
- package/dist/boss-store.js.map +0 -1
- package/dist/boss-system-prompt.d.ts +0 -3
- package/dist/boss-system-prompt.d.ts.map +0 -1
- package/dist/boss-system-prompt.js +0 -180
- package/dist/boss-system-prompt.js.map +0 -1
- package/dist/boss-tasks-overlay.d.ts +0 -22
- package/dist/boss-tasks-overlay.d.ts.map +0 -1
- package/dist/boss-tasks-overlay.js +0 -157
- package/dist/boss-tasks-overlay.js.map +0 -1
- package/dist/branding.d.ts +0 -32
- package/dist/branding.d.ts.map +0 -1
- package/dist/branding.js +0 -59
- package/dist/branding.js.map +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.smoke.test.d.ts +0 -2
- package/dist/cli.smoke.test.d.ts.map +0 -1
- package/dist/cli.smoke.test.js +0 -48
- package/dist/cli.smoke.test.js.map +0 -1
- package/dist/colors.d.ts +0 -14
- package/dist/colors.d.ts.map +0 -1
- package/dist/colors.js +0 -31
- package/dist/colors.js.map +0 -1
- package/dist/discover.d.ts +0 -13
- package/dist/discover.d.ts.map +0 -1
- package/dist/discover.js +0 -92
- package/dist/discover.js.map +0 -1
- package/dist/event-queue.d.ts +0 -16
- package/dist/event-queue.d.ts.map +0 -1
- package/dist/event-queue.js +0 -39
- package/dist/event-queue.js.map +0 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.d.ts.map +0 -1
- package/dist/link-command.d.ts +0 -2
- package/dist/link-command.d.ts.map +0 -1
- package/dist/link-command.js +0 -120
- package/dist/link-command.js.map +0 -1
- package/dist/links.d.ts +0 -11
- package/dist/links.d.ts.map +0 -1
- package/dist/links.js +0 -22
- package/dist/links.js.map +0 -1
- package/dist/logger.d.ts +0 -41
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -112
- package/dist/logger.js.map +0 -1
- package/dist/orchestrator-app.d.ts +0 -15
- package/dist/orchestrator-app.d.ts.map +0 -1
- package/dist/orchestrator-app.js +0 -599
- package/dist/orchestrator-app.js.map +0 -1
- package/dist/orchestrator.d.ts +0 -147
- package/dist/orchestrator.d.ts.map +0 -1
- package/dist/orchestrator.js +0 -707
- package/dist/orchestrator.js.map +0 -1
- package/dist/orchestrator.test.d.ts +0 -2
- package/dist/orchestrator.test.d.ts.map +0 -1
- package/dist/orchestrator.test.js +0 -55
- package/dist/orchestrator.test.js.map +0 -1
- package/dist/pixel-WB6VRJWP.js +0 -13
- package/dist/radio-picker.d.ts +0 -20
- package/dist/radio-picker.d.ts.map +0 -1
- package/dist/radio-picker.js +0 -31
- package/dist/radio-picker.js.map +0 -1
- package/dist/radio.d.ts +0 -43
- package/dist/radio.d.ts.map +0 -1
- package/dist/radio.js +0 -150
- package/dist/radio.js.map +0 -1
- package/dist/sessions.d.ts +0 -21
- package/dist/sessions.d.ts.map +0 -1
- package/dist/sessions.js +0 -122
- package/dist/sessions.js.map +0 -1
- package/dist/settings.d.ts +0 -11
- package/dist/settings.d.ts.map +0 -1
- package/dist/settings.js +0 -38
- package/dist/settings.js.map +0 -1
- package/dist/slash-commands.d.ts +0 -19
- package/dist/slash-commands.d.ts.map +0 -1
- package/dist/slash-commands.js +0 -76
- package/dist/slash-commands.js.map +0 -1
- package/dist/splash.d.ts +0 -21
- package/dist/splash.d.ts.map +0 -1
- package/dist/splash.js +0 -137
- package/dist/splash.js.map +0 -1
- package/dist/task-tools.d.ts +0 -18
- package/dist/task-tools.d.ts.map +0 -1
- package/dist/task-tools.js +0 -172
- package/dist/task-tools.js.map +0 -1
- package/dist/tasks-store.d.ts +0 -66
- package/dist/tasks-store.d.ts.map +0 -1
- package/dist/tasks-store.js +0 -199
- package/dist/tasks-store.js.map +0 -1
- package/dist/tasks-store.test.d.ts +0 -2
- package/dist/tasks-store.test.d.ts.map +0 -1
- package/dist/tasks-store.test.js +0 -138
- package/dist/tasks-store.test.js.map +0 -1
- package/dist/tool-formatters.d.ts +0 -7
- package/dist/tool-formatters.d.ts.map +0 -1
- package/dist/tool-formatters.js +0 -111
- package/dist/tool-formatters.js.map +0 -1
- package/dist/tools.d.ts +0 -26
- package/dist/tools.d.ts.map +0 -1
- package/dist/tools.js +0 -133
- package/dist/tools.js.map +0 -1
- package/dist/types.d.ts +0 -32
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/worker.d.ts +0 -47
- package/dist/worker.d.ts.map +0 -1
- package/dist/worker.js +0 -123
- package/dist/worker.js.map +0 -1
- /package/dist/{pixel-WB6VRJWP.js.map → pixel-WPYTQADG.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,203 +1,2269 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
ActivityIndicator,
|
|
5
|
+
AnimationProvider,
|
|
6
|
+
AssistantMessage,
|
|
7
|
+
Box_default,
|
|
8
|
+
CompactionDone,
|
|
9
|
+
CompactionSpinner,
|
|
10
|
+
GGBoss,
|
|
11
|
+
InputArea,
|
|
12
|
+
MessageResponse,
|
|
13
|
+
ModelSelector,
|
|
14
|
+
SelectList,
|
|
15
|
+
Static,
|
|
16
|
+
StreamingArea,
|
|
17
|
+
TerminalSizeProvider,
|
|
18
|
+
Text,
|
|
19
|
+
ThemeContext,
|
|
20
|
+
ToolExecution,
|
|
21
|
+
ToolUseLoader,
|
|
22
|
+
UserMessage,
|
|
23
|
+
bossStore,
|
|
24
|
+
getAppPaths,
|
|
25
|
+
getContextWindow,
|
|
26
|
+
getSplashAudioDurationMs,
|
|
27
|
+
initLogger,
|
|
28
|
+
loadSettings,
|
|
29
|
+
loadTheme,
|
|
30
|
+
log,
|
|
31
|
+
playSplashAudio,
|
|
32
|
+
render_default,
|
|
33
|
+
require_jsx_runtime,
|
|
34
|
+
require_react,
|
|
35
|
+
tasksStore,
|
|
36
|
+
useAnimationActive,
|
|
37
|
+
useAnimationTick,
|
|
38
|
+
useBossState,
|
|
39
|
+
useDoublePress,
|
|
40
|
+
useTasksState,
|
|
41
|
+
useTerminalSize,
|
|
42
|
+
useTheme,
|
|
43
|
+
use_app_default,
|
|
44
|
+
use_input_default,
|
|
45
|
+
use_stdout_default
|
|
46
|
+
} from "./chunk-3EWLK53W.js";
|
|
47
|
+
import "./chunk-QT366Y52.js";
|
|
48
|
+
import {
|
|
49
|
+
source_default
|
|
50
|
+
} from "./chunk-WJ4S4TOY.js";
|
|
51
|
+
import {
|
|
52
|
+
__toESM,
|
|
53
|
+
init_esm_shims
|
|
54
|
+
} from "./chunk-YNWFCUMR.js";
|
|
55
|
+
|
|
56
|
+
// src/cli.ts
|
|
57
|
+
init_esm_shims();
|
|
58
|
+
import path4 from "path";
|
|
59
|
+
|
|
60
|
+
// src/links.ts
|
|
61
|
+
init_esm_shims();
|
|
62
|
+
import fs from "fs/promises";
|
|
63
|
+
import path from "path";
|
|
64
|
+
function getLinksPath() {
|
|
65
|
+
return path.join(getAppPaths().agentDir, "boss", "links.json");
|
|
66
|
+
}
|
|
67
|
+
async function loadLinks() {
|
|
68
|
+
try {
|
|
69
|
+
const content = await fs.readFile(getLinksPath(), "utf-8");
|
|
70
|
+
const parsed = JSON.parse(content);
|
|
71
|
+
return { projects: parsed.projects ?? [] };
|
|
72
|
+
} catch {
|
|
73
|
+
return { projects: [] };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function saveLinks(links) {
|
|
77
|
+
const p = getLinksPath();
|
|
78
|
+
await fs.mkdir(path.dirname(p), { recursive: true, mode: 448 });
|
|
79
|
+
await fs.writeFile(p, JSON.stringify(links, null, 2), "utf-8");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/link-command.tsx
|
|
83
|
+
init_esm_shims();
|
|
84
|
+
var import_react2 = __toESM(require_react(), 1);
|
|
85
|
+
|
|
86
|
+
// src/discover.ts
|
|
87
|
+
init_esm_shims();
|
|
88
|
+
import fs2 from "fs/promises";
|
|
89
|
+
import path2 from "path";
|
|
90
|
+
async function discoverProjects() {
|
|
91
|
+
const sessionsDir = getAppPaths().sessionsDir;
|
|
92
|
+
let entries;
|
|
93
|
+
try {
|
|
94
|
+
entries = await fs2.readdir(sessionsDir);
|
|
95
|
+
} catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
const results = [];
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
const dir = path2.join(sessionsDir, entry);
|
|
101
|
+
let stat;
|
|
102
|
+
try {
|
|
103
|
+
stat = await fs2.stat(dir);
|
|
104
|
+
} catch {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (!stat.isDirectory()) continue;
|
|
108
|
+
let files;
|
|
109
|
+
try {
|
|
110
|
+
files = await fs2.readdir(dir);
|
|
111
|
+
} catch {
|
|
112
|
+
continue;
|
|
21
113
|
}
|
|
22
|
-
const
|
|
23
|
-
|
|
114
|
+
const sessionFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
115
|
+
if (sessionFiles.length === 0) continue;
|
|
116
|
+
let maxMtime = 0;
|
|
117
|
+
for (const f of sessionFiles) {
|
|
118
|
+
try {
|
|
119
|
+
const s = await fs2.stat(path2.join(dir, f));
|
|
120
|
+
if (s.mtimeMs > maxMtime) maxMtime = s.mtimeMs;
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const decoded = "/" + entry.replace(/_/g, "/");
|
|
125
|
+
try {
|
|
126
|
+
const pathStat = await fs2.stat(decoded);
|
|
127
|
+
if (!pathStat.isDirectory()) continue;
|
|
128
|
+
} catch {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
results.push({
|
|
132
|
+
name: path2.basename(decoded),
|
|
133
|
+
path: decoded,
|
|
134
|
+
lastActiveMs: maxMtime,
|
|
135
|
+
lastActiveDisplay: formatRelativeTime(maxMtime)
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
results.sort((a, b) => b.lastActiveMs - a.lastActiveMs);
|
|
139
|
+
return results;
|
|
24
140
|
}
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
141
|
+
function formatRelativeTime(ms) {
|
|
142
|
+
if (ms === 0) return "\u2014";
|
|
143
|
+
const diff = Date.now() - ms;
|
|
144
|
+
if (diff < 6e4) return "just now";
|
|
145
|
+
const min = 6e4;
|
|
146
|
+
const hour = 60 * min;
|
|
147
|
+
const day = 24 * hour;
|
|
148
|
+
const week = 7 * day;
|
|
149
|
+
const month = 30 * day;
|
|
150
|
+
if (diff < hour) return `${Math.floor(diff / min)}m ago`;
|
|
151
|
+
if (diff < day) return `${Math.floor(diff / hour)}h ago`;
|
|
152
|
+
if (diff < week) return `${Math.floor(diff / day)}d ago`;
|
|
153
|
+
if (diff < month) return `${Math.floor(diff / week)}w ago`;
|
|
154
|
+
return `${Math.floor(diff / month)}mo ago`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/banner.tsx
|
|
158
|
+
init_esm_shims();
|
|
159
|
+
var import_react = __toESM(require_react(), 1);
|
|
160
|
+
|
|
161
|
+
// src/branding.ts
|
|
162
|
+
init_esm_shims();
|
|
163
|
+
|
|
164
|
+
// package.json
|
|
165
|
+
var package_default = {
|
|
166
|
+
name: "@kenkaiiii/gg-boss",
|
|
167
|
+
version: "4.3.142",
|
|
168
|
+
type: "module",
|
|
169
|
+
description: "Orchestrator agent that drives multiple ggcoder sessions across projects from a single chat",
|
|
170
|
+
license: "MIT",
|
|
171
|
+
repository: {
|
|
172
|
+
type: "git",
|
|
173
|
+
url: "git+https://github.com/kenkaiiii/gg-framework.git",
|
|
174
|
+
directory: "packages/gg-boss"
|
|
175
|
+
},
|
|
176
|
+
bin: {
|
|
177
|
+
ggboss: "./dist/cli.js"
|
|
178
|
+
},
|
|
179
|
+
exports: {
|
|
180
|
+
".": {
|
|
181
|
+
import: "./dist/index.js",
|
|
182
|
+
types: "./dist/index.d.ts"
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
files: [
|
|
186
|
+
"dist"
|
|
187
|
+
],
|
|
188
|
+
scripts: {
|
|
189
|
+
build: "tsup && cp -R assets/. dist/ && chmod +x dist/cli.js",
|
|
190
|
+
check: "tsc --noEmit",
|
|
191
|
+
test: "vitest run"
|
|
192
|
+
},
|
|
193
|
+
devDependencies: {
|
|
194
|
+
"@kenkaiiii/gg-agent": "workspace:*",
|
|
195
|
+
"@kenkaiiii/gg-ai": "workspace:*",
|
|
196
|
+
"@kenkaiiii/ggcoder": "workspace:*",
|
|
197
|
+
"@types/node": "^25.6.0",
|
|
198
|
+
"@types/react": "^19.2.14",
|
|
199
|
+
chalk: "^5.6.2",
|
|
200
|
+
ink: "^7.0.2",
|
|
201
|
+
react: "^19.2.5",
|
|
202
|
+
tsup: "^8.5.1",
|
|
203
|
+
typescript: "^6.0.3",
|
|
204
|
+
vitest: "^4.1.4",
|
|
205
|
+
zod: "^4.4.3"
|
|
206
|
+
},
|
|
207
|
+
publishConfig: {
|
|
208
|
+
access: "public"
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/branding.ts
|
|
213
|
+
var VERSION = package_default.version;
|
|
214
|
+
var BRAND = "GG Boss";
|
|
215
|
+
var AUTHOR = "Ken Kai";
|
|
216
|
+
var LOGO_LINES = [" \u2584\u2580\u2580\u2580 \u2584\u2580\u2580\u2580", " \u2588 \u2580\u2588 \u2588 \u2580\u2588", " \u2580\u2584\u2584\u2580 \u2580\u2584\u2584\u2580"];
|
|
217
|
+
var LOGO_GAP = " ";
|
|
218
|
+
var GRADIENT = [
|
|
219
|
+
"#dc2626",
|
|
220
|
+
// red-600
|
|
221
|
+
"#e11d48",
|
|
222
|
+
// rose-600
|
|
223
|
+
"#be185d",
|
|
224
|
+
// pink-700
|
|
225
|
+
"#a21caf",
|
|
226
|
+
// fuchsia-700
|
|
227
|
+
"#c026d3",
|
|
228
|
+
// fuchsia-600
|
|
229
|
+
"#d946ef",
|
|
230
|
+
// fuchsia-500
|
|
231
|
+
"#c026d3",
|
|
232
|
+
// fuchsia-600 (back)
|
|
233
|
+
"#a21caf",
|
|
234
|
+
// fuchsia-700 (back)
|
|
235
|
+
"#be185d",
|
|
236
|
+
// pink-700 (back)
|
|
237
|
+
"#e11d48",
|
|
238
|
+
// rose-600 (back)
|
|
239
|
+
"#dc2626",
|
|
240
|
+
// red-600 (back)
|
|
241
|
+
"#b91c1c"
|
|
242
|
+
// red-700 (slight darker tail)
|
|
243
|
+
];
|
|
244
|
+
var PULSE_COLORS = [
|
|
245
|
+
"#dc2626",
|
|
246
|
+
// crimson
|
|
247
|
+
"#e11d48",
|
|
248
|
+
// rose
|
|
249
|
+
"#be185d",
|
|
250
|
+
// wine
|
|
251
|
+
"#a21caf",
|
|
252
|
+
// magenta
|
|
253
|
+
"#c026d3",
|
|
254
|
+
// fuchsia
|
|
255
|
+
"#a21caf",
|
|
256
|
+
// back
|
|
257
|
+
"#be185d",
|
|
258
|
+
// back
|
|
259
|
+
"#e11d48"
|
|
260
|
+
// back
|
|
261
|
+
];
|
|
262
|
+
var COLORS = {
|
|
263
|
+
primary: "#e11d48",
|
|
264
|
+
// crimson-rose — main brand color
|
|
265
|
+
accent: "#d946ef",
|
|
266
|
+
// fuchsia — secondary
|
|
267
|
+
text: "#e2e8f0",
|
|
268
|
+
textDim: "#6b7280",
|
|
269
|
+
success: "#4ade80",
|
|
270
|
+
warning: "#fbbf24",
|
|
271
|
+
error: "#f87171"
|
|
272
|
+
};
|
|
273
|
+
function clearScreen() {
|
|
274
|
+
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/banner.tsx
|
|
278
|
+
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
279
|
+
function BossBanner({ subtitle, hint, showShortcuts }) {
|
|
280
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [
|
|
281
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
|
|
282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GradientText, { text: LOGO_LINES[0] }),
|
|
283
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: LOGO_GAP }),
|
|
284
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.primary, bold: true, children: BRAND }),
|
|
285
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { color: COLORS.textDim, children: [
|
|
286
|
+
" v",
|
|
287
|
+
VERSION
|
|
288
|
+
] }),
|
|
289
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.textDim, children: " \xB7 By " }),
|
|
290
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.text, bold: true, children: AUTHOR })
|
|
291
|
+
] }),
|
|
292
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
|
|
293
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GradientText, { text: LOGO_LINES[1] }),
|
|
294
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: LOGO_GAP }),
|
|
295
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.accent, children: subtitle })
|
|
296
|
+
] }),
|
|
297
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
|
|
298
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GradientText, { text: LOGO_LINES[2] }),
|
|
299
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: LOGO_GAP }),
|
|
300
|
+
showShortcuts ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
301
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.primary, children: "^T" }),
|
|
302
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.textDim, children: " tasks" }),
|
|
303
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.textDim, children: " " }),
|
|
304
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.primary, children: "Tab" }),
|
|
305
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.textDim, children: " scope" }),
|
|
306
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.textDim, children: " " }),
|
|
307
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.primary, children: "\u21E7Tab" }),
|
|
308
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.textDim, children: " thinking" }),
|
|
309
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.textDim, children: " " }),
|
|
310
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.primary, children: "ESC" }),
|
|
311
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.textDim, children: " interrupt" })
|
|
312
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: COLORS.textDim, children: hint ?? "" })
|
|
313
|
+
] })
|
|
314
|
+
] });
|
|
315
|
+
}
|
|
316
|
+
function GradientText({ text }) {
|
|
317
|
+
const chars = [];
|
|
318
|
+
let colorIdx = 0;
|
|
319
|
+
for (let i = 0; i < text.length; i++) {
|
|
320
|
+
const ch = text[i];
|
|
321
|
+
if (ch === " ") {
|
|
322
|
+
chars.push(ch);
|
|
323
|
+
} else {
|
|
324
|
+
const color = GRADIENT[colorIdx % GRADIENT.length];
|
|
325
|
+
chars.push(
|
|
326
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color, children: ch }, i)
|
|
327
|
+
);
|
|
328
|
+
colorIdx++;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: chars });
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/link-command.tsx
|
|
335
|
+
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
336
|
+
var VISIBLE_ROWS = 12;
|
|
337
|
+
function LinkScreen({ projects, initialSelected, onDone }) {
|
|
338
|
+
const [cursor, setCursor] = (0, import_react2.useState)(0);
|
|
339
|
+
const [selected, setSelected] = (0, import_react2.useState)(new Set(initialSelected));
|
|
340
|
+
const [scrollOffset, setScrollOffset] = (0, import_react2.useState)(0);
|
|
341
|
+
const visible = projects.slice(scrollOffset, scrollOffset + VISIBLE_ROWS);
|
|
342
|
+
use_input_default((input, key) => {
|
|
343
|
+
if (key.ctrl && input === "c") {
|
|
344
|
+
onDone([], true);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (projects.length === 0) {
|
|
348
|
+
if (key.return || key.escape || input === "q") onDone([], true);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (key.upArrow) {
|
|
352
|
+
const next = Math.max(0, cursor - 1);
|
|
353
|
+
setCursor(next);
|
|
354
|
+
if (next < scrollOffset) setScrollOffset(next);
|
|
355
|
+
} else if (key.downArrow) {
|
|
356
|
+
const next = Math.min(projects.length - 1, cursor + 1);
|
|
357
|
+
setCursor(next);
|
|
358
|
+
if (next >= scrollOffset + VISIBLE_ROWS) setScrollOffset(next - VISIBLE_ROWS + 1);
|
|
359
|
+
} else if (input === " ") {
|
|
360
|
+
const p = projects[cursor];
|
|
361
|
+
if (!p) return;
|
|
362
|
+
const nextSet = new Set(selected);
|
|
363
|
+
if (nextSet.has(p.path)) nextSet.delete(p.path);
|
|
364
|
+
else nextSet.add(p.path);
|
|
365
|
+
setSelected(nextSet);
|
|
366
|
+
} else if (input === "a") {
|
|
367
|
+
const allSelected = projects.every((p) => selected.has(p.path));
|
|
368
|
+
setSelected(allSelected ? /* @__PURE__ */ new Set() : new Set(projects.map((p) => p.path)));
|
|
369
|
+
} else if (key.return) {
|
|
370
|
+
onDone(
|
|
371
|
+
projects.filter((p) => selected.has(p.path)).map((p) => p.path),
|
|
372
|
+
false
|
|
373
|
+
);
|
|
374
|
+
} else if (key.escape || input === "q") {
|
|
375
|
+
onDone([], true);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
const subtitle = projects.length === 0 ? "Link projects" : `Link projects \xB7 ${projects.length} discovered \xB7 ${selected.size} selected`;
|
|
379
|
+
const hint = "\u2191\u2193 navigate \xB7 space toggle \xB7 a all \xB7 enter save \xB7 esc cancel";
|
|
380
|
+
if (projects.length === 0) {
|
|
381
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingX: 2, children: [
|
|
382
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BossBanner, { subtitle: "Link projects", hint: "No projects yet" }),
|
|
383
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", marginLeft: 2, children: [
|
|
384
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.textDim, children: "No ggcoder projects found in ~/.gg/sessions/." }),
|
|
385
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: COLORS.textDim, children: [
|
|
386
|
+
"Run ggcoder in a project at least once, then re-run",
|
|
387
|
+
" ",
|
|
388
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.accent, children: "ggboss link" }),
|
|
389
|
+
"."
|
|
390
|
+
] }),
|
|
391
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.textDim, children: "Press any key to exit." }) })
|
|
392
|
+
] })
|
|
393
|
+
] });
|
|
394
|
+
}
|
|
395
|
+
const showingTop = scrollOffset > 0;
|
|
396
|
+
const showingBottom = scrollOffset + VISIBLE_ROWS < projects.length;
|
|
397
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingX: 2, children: [
|
|
398
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BossBanner, { subtitle, hint }),
|
|
399
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", marginLeft: 2, children: [
|
|
400
|
+
showingTop && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.textDim, children: " \u2191 more above" }),
|
|
401
|
+
visible.map((p, i) => {
|
|
402
|
+
const realIndex = scrollOffset + i;
|
|
403
|
+
const isCursor = realIndex === cursor;
|
|
404
|
+
const isSelected = selected.has(p.path);
|
|
405
|
+
const checkbox = isSelected ? "[\u2713]" : "[ ]";
|
|
406
|
+
const arrow = isCursor ? "\u276F" : " ";
|
|
407
|
+
const nameColor = isCursor ? COLORS.primary : isSelected ? COLORS.success : COLORS.text;
|
|
408
|
+
const checkboxColor = isSelected ? COLORS.success : COLORS.textDim;
|
|
409
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { children: [
|
|
410
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.primary, children: arrow }),
|
|
411
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
412
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: checkboxColor, children: checkbox }),
|
|
413
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
414
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: nameColor, bold: isCursor, children: p.name }),
|
|
415
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.textDim, children: " " }),
|
|
416
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.textDim, children: p.lastActiveDisplay })
|
|
417
|
+
] }, p.path);
|
|
418
|
+
}),
|
|
419
|
+
showingBottom && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.textDim, children: " \u2193 more below" })
|
|
420
|
+
] })
|
|
421
|
+
] });
|
|
422
|
+
}
|
|
423
|
+
function LinkApp({ projects, initialSelected, resolve }) {
|
|
424
|
+
const { exit } = use_app_default();
|
|
425
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
426
|
+
LinkScreen,
|
|
427
|
+
{
|
|
428
|
+
projects,
|
|
429
|
+
initialSelected,
|
|
430
|
+
onDone: (selected, cancelled) => {
|
|
431
|
+
resolve({ selected, cancelled });
|
|
432
|
+
exit();
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
async function runLinkCommand() {
|
|
438
|
+
const projects = await discoverProjects();
|
|
439
|
+
const links = await loadLinks();
|
|
440
|
+
const initialSelected = new Set(links.projects.map((p) => p.cwd));
|
|
441
|
+
clearScreen();
|
|
442
|
+
const result = await new Promise((resolve) => {
|
|
443
|
+
const { waitUntilExit } = render_default(
|
|
444
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LinkApp, { projects, initialSelected, resolve })
|
|
445
|
+
);
|
|
446
|
+
void waitUntilExit();
|
|
447
|
+
});
|
|
448
|
+
if (result.cancelled) {
|
|
449
|
+
process.stdout.write(source_default.hex(COLORS.textDim)("\nCancelled. No changes saved.\n"));
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const linked = result.selected.map((path5) => projects.find((p) => p.path === path5)).filter((p) => Boolean(p)).map((p) => ({ name: p.name, cwd: p.path }));
|
|
453
|
+
await saveLinks({ projects: linked });
|
|
454
|
+
process.stdout.write("\n");
|
|
455
|
+
if (linked.length === 0) {
|
|
456
|
+
process.stdout.write(source_default.hex(COLORS.warning)("Cleared linked projects.\n"));
|
|
457
|
+
} else {
|
|
458
|
+
process.stdout.write(
|
|
459
|
+
source_default.hex(COLORS.success)(
|
|
460
|
+
`Linked ${linked.length} project${linked.length === 1 ? "" : "s"}:
|
|
461
|
+
`
|
|
462
|
+
)
|
|
463
|
+
);
|
|
464
|
+
for (const p of linked) {
|
|
465
|
+
process.stdout.write(
|
|
466
|
+
" " + source_default.hex(COLORS.primary)("\xB7") + " " + source_default.hex(COLORS.text)(p.name) + "\n"
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
process.stdout.write("\n");
|
|
470
|
+
process.stdout.write(
|
|
471
|
+
source_default.hex(COLORS.textDim)(`Run `) + source_default.hex(COLORS.accent)("ggboss") + source_default.hex(COLORS.textDim)(` to start the orchestrator.
|
|
472
|
+
`)
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/orchestrator-app.tsx
|
|
478
|
+
init_esm_shims();
|
|
479
|
+
var import_react11 = __toESM(require_react(), 1);
|
|
480
|
+
|
|
481
|
+
// ../ggcoder/dist/ui/components/index.js
|
|
482
|
+
init_esm_shims();
|
|
483
|
+
|
|
484
|
+
// ../ggcoder/dist/ui/components/DiffView.js
|
|
485
|
+
init_esm_shims();
|
|
486
|
+
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
487
|
+
var import_react3 = __toESM(require_react(), 1);
|
|
488
|
+
|
|
489
|
+
// ../ggcoder/dist/ui/components/Overlay.js
|
|
490
|
+
init_esm_shims();
|
|
491
|
+
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
|
|
492
|
+
var import_react4 = __toESM(require_react(), 1);
|
|
493
|
+
|
|
494
|
+
// ../ggcoder/dist/ui/components/SessionSelector.js
|
|
495
|
+
init_esm_shims();
|
|
496
|
+
var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
|
|
497
|
+
var import_react5 = __toESM(require_react(), 1);
|
|
498
|
+
|
|
499
|
+
// ../ggcoder/dist/ui/components/SettingsSelector.js
|
|
500
|
+
init_esm_shims();
|
|
501
|
+
var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
|
|
502
|
+
var import_react6 = __toESM(require_react(), 1);
|
|
503
|
+
|
|
504
|
+
// ../ggcoder/dist/ui/components/ThinkingIndicator.js
|
|
505
|
+
init_esm_shims();
|
|
506
|
+
var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
|
|
507
|
+
var import_react7 = __toESM(require_react(), 1);
|
|
508
|
+
|
|
509
|
+
// src/boss-footer.tsx
|
|
510
|
+
init_esm_shims();
|
|
511
|
+
var import_react8 = __toESM(require_react(), 1);
|
|
512
|
+
var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
|
|
513
|
+
var PARTIAL_BLOCKS = [" ", "\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u258A", "\u2589", "\u2588"];
|
|
514
|
+
var LIGHT_SHADE = "\u2591";
|
|
515
|
+
var SHORT_MODELS = {
|
|
516
|
+
"claude-opus-4-7": "Opus",
|
|
517
|
+
"claude-sonnet-4-6": "Sonnet",
|
|
518
|
+
"claude-haiku-4-5": "Haiku",
|
|
519
|
+
"claude-haiku-4-5-20251001": "Haiku",
|
|
520
|
+
"gpt-5.5": "GPT-5.5",
|
|
521
|
+
"gpt-5.5-pro": "GPT-5.5 Pro",
|
|
522
|
+
"gpt-5.4": "GPT-5.4",
|
|
523
|
+
"gpt-5.4-mini": "GPT-5.4 Mini",
|
|
524
|
+
"gpt-5.3-codex": "GPT-5.3 Codex"
|
|
525
|
+
};
|
|
526
|
+
function shortModel(model) {
|
|
527
|
+
return SHORT_MODELS[model] ?? model;
|
|
528
|
+
}
|
|
529
|
+
function getContextPercent(model, tokensIn) {
|
|
530
|
+
const limit = getContextWindow(model);
|
|
531
|
+
if (!limit || tokensIn === 0) return 0;
|
|
532
|
+
return Math.round(tokensIn / limit * 100);
|
|
533
|
+
}
|
|
534
|
+
var SHORT_RADIO = {
|
|
535
|
+
"somafm-groove-salad": "Groove Salad",
|
|
536
|
+
"somafm-drone-zone": "Drone Zone",
|
|
537
|
+
"radio-paradise": "Radio Paradise",
|
|
538
|
+
"george-fm": "George FM"
|
|
539
|
+
};
|
|
540
|
+
function BossFooter({
|
|
541
|
+
bossModel,
|
|
542
|
+
workerModel,
|
|
543
|
+
tokensIn,
|
|
544
|
+
exitPending,
|
|
545
|
+
bossThinkingLevel,
|
|
546
|
+
updatePending,
|
|
547
|
+
currentRadioStationId
|
|
548
|
+
}) {
|
|
549
|
+
const theme = useTheme();
|
|
550
|
+
const { columns } = useTerminalSize();
|
|
551
|
+
if (exitPending) {
|
|
552
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.warning, children: "Press Ctrl+C again to exit" }) });
|
|
553
|
+
}
|
|
554
|
+
const contextPct = getContextPercent(bossModel, tokensIn);
|
|
555
|
+
const contextColor = contextPct >= 80 ? theme.error : contextPct >= 50 ? theme.warning : theme.success;
|
|
556
|
+
const sep = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.border, children: " \u2502 " });
|
|
557
|
+
const barWidth = 8;
|
|
558
|
+
const fillFloat = Math.min(contextPct / 100 * barWidth, barWidth);
|
|
559
|
+
const barChars = [];
|
|
560
|
+
for (let i = 0; i < barWidth; i++) {
|
|
561
|
+
const cellFill = Math.max(0, Math.min(1, fillFloat - i));
|
|
562
|
+
const eighths = Math.round(cellFill * 8);
|
|
563
|
+
if (eighths === 8) {
|
|
564
|
+
barChars.push(
|
|
565
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: contextColor, children: PARTIAL_BLOCKS[8] }, i)
|
|
566
|
+
);
|
|
567
|
+
} else if (eighths > 0) {
|
|
568
|
+
barChars.push(
|
|
569
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: contextColor, children: PARTIAL_BLOCKS[eighths] }, i)
|
|
570
|
+
);
|
|
571
|
+
} else {
|
|
572
|
+
barChars.push(
|
|
573
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: LIGHT_SHADE }, i)
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const radioName = currentRadioStationId ? SHORT_RADIO[currentRadioStationId] ?? currentRadioStationId : null;
|
|
578
|
+
const bossM = shortModel(bossModel);
|
|
579
|
+
const wkrM = shortModel(workerModel);
|
|
580
|
+
const estFull = 2 + 12 + // bar + " 99%"
|
|
581
|
+
3 + 5 + bossM.length + // " │ boss <model>"
|
|
582
|
+
3 + 8 + wkrM.length + // " │ workers <model>"
|
|
583
|
+
3 + 12 + // " │ Thinking off"
|
|
584
|
+
(radioName ? 3 + 2 + radioName.length : 0) + // " │ ♪ Name"
|
|
585
|
+
(updatePending ? 3 + 28 : 0);
|
|
586
|
+
const dropLabels = estFull > columns;
|
|
587
|
+
const dropThinking = estFull > columns + 14;
|
|
588
|
+
const useShortUpdate = updatePending && estFull > columns + 6;
|
|
589
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { paddingX: 1, width: columns, children: [
|
|
590
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { flexGrow: 1 }),
|
|
591
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexShrink: 0, children: [
|
|
592
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: barChars }),
|
|
593
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: contextColor, children: [
|
|
594
|
+
" ",
|
|
595
|
+
contextPct,
|
|
596
|
+
"%"
|
|
597
|
+
] }),
|
|
598
|
+
sep,
|
|
599
|
+
!dropLabels && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: "boss " }),
|
|
600
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: COLORS.primary, bold: true, children: bossM }),
|
|
601
|
+
sep,
|
|
602
|
+
!dropLabels && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: "workers " }),
|
|
603
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: COLORS.accent, bold: true, children: wkrM }),
|
|
604
|
+
!dropThinking && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
605
|
+
sep,
|
|
606
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: bossThinkingLevel ? theme.accent : theme.textDim, children: bossThinkingLevel ? "Thinking on" : "Thinking off" })
|
|
607
|
+
] }),
|
|
608
|
+
radioName && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
609
|
+
sep,
|
|
610
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.secondary ?? theme.accent, children: [
|
|
611
|
+
"\u266A ",
|
|
612
|
+
radioName
|
|
613
|
+
] })
|
|
614
|
+
] }),
|
|
615
|
+
updatePending && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
616
|
+
sep,
|
|
617
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.success, bold: true, wrap: "truncate", children: useShortUpdate ? "Update ready" : "Update ready. Restart GG Boss." })
|
|
618
|
+
] })
|
|
619
|
+
] })
|
|
620
|
+
] });
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/slash-commands.ts
|
|
624
|
+
init_esm_shims();
|
|
625
|
+
var BOSS_SLASH_COMMANDS = [
|
|
626
|
+
{ name: "help", aliases: ["?"], description: "Show available commands" },
|
|
627
|
+
{ name: "model-boss", aliases: [], description: "Switch the orchestrator's model" },
|
|
628
|
+
{ name: "model-workers", aliases: [], description: "Switch every worker's model" },
|
|
629
|
+
{ name: "compact", aliases: [], description: "Compact the boss's context now" },
|
|
630
|
+
{ name: "clear", aliases: [], description: "Clear chat history and terminal" },
|
|
631
|
+
{ name: "radio", aliases: [], description: "Stream a free internet radio station" },
|
|
632
|
+
{ name: "quit", aliases: ["q", "exit"], description: "Exit gg-boss" }
|
|
633
|
+
];
|
|
634
|
+
function isSlashCommand(value) {
|
|
635
|
+
return value.startsWith("/") && !value.startsWith("//");
|
|
636
|
+
}
|
|
637
|
+
function parseSlash(value) {
|
|
638
|
+
if (!isSlashCommand(value)) return null;
|
|
639
|
+
const rest = value.slice(1).trim();
|
|
640
|
+
if (!rest) return null;
|
|
641
|
+
const space = rest.indexOf(" ");
|
|
642
|
+
if (space === -1) return { name: rest.toLowerCase(), args: "" };
|
|
643
|
+
return { name: rest.slice(0, space).toLowerCase(), args: rest.slice(space + 1).trim() };
|
|
644
|
+
}
|
|
645
|
+
function canonicalName(name) {
|
|
646
|
+
for (const cmd of BOSS_SLASH_COMMANDS) {
|
|
647
|
+
if (cmd.name === name) return cmd.name;
|
|
648
|
+
if (cmd.aliases.includes(name)) return cmd.name;
|
|
649
|
+
}
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
function buildHelpText() {
|
|
653
|
+
const lines = ["**gg-boss commands**", ""];
|
|
654
|
+
for (const cmd of BOSS_SLASH_COMMANDS) {
|
|
655
|
+
const aliases = cmd.aliases.length > 0 ? ` (${cmd.aliases.map((a) => "/" + a).join(", ")})` : "";
|
|
656
|
+
lines.push(`- \`/${cmd.name}\`${aliases} \u2014 ${cmd.description}`);
|
|
657
|
+
}
|
|
658
|
+
lines.push("");
|
|
659
|
+
lines.push("**Global keybindings**");
|
|
660
|
+
lines.push("- `Ctrl+T` \u2014 open the Tasks pane");
|
|
661
|
+
lines.push("- `Tab` \u2014 switch project scope (All / per-project pill in the input)");
|
|
662
|
+
lines.push("- `Shift+Tab` \u2014 toggle the boss's extended thinking on/off");
|
|
663
|
+
lines.push("- `Esc` \u2014 interrupt the boss while it's running");
|
|
664
|
+
lines.push("- `Ctrl+C` (twice) \u2014 exit");
|
|
665
|
+
lines.push("");
|
|
666
|
+
lines.push("**Inside the Tasks pane (Ctrl+T)**");
|
|
667
|
+
lines.push("- `\u2191` / `\u2193` (or `k` / `j`) \u2014 navigate tasks");
|
|
668
|
+
lines.push("- `r` \u2014 run all pending and blocked tasks across idle workers");
|
|
669
|
+
lines.push("- `d` \u2014 delete the selected task");
|
|
670
|
+
lines.push("- `Esc` \u2014 close the Tasks pane");
|
|
671
|
+
lines.push("");
|
|
672
|
+
lines.push("**Inside model pickers (`/model-boss`, `/model-workers`)**");
|
|
673
|
+
lines.push("- `\u2191` / `\u2193` \u2014 navigate models");
|
|
674
|
+
lines.push("- `Enter` \u2014 select");
|
|
675
|
+
lines.push("- `Esc` \u2014 cancel");
|
|
676
|
+
lines.push("");
|
|
677
|
+
lines.push("**Radio** (`/radio`)");
|
|
678
|
+
lines.push("- Pick a station to stream while you work, or select `Off` to stop.");
|
|
679
|
+
lines.push("- Requires `mpv` (recommended), `ffplay`, `mpg123`, or `vlc/cvlc` installed.");
|
|
680
|
+
lines.push("");
|
|
681
|
+
lines.push("**Input area**");
|
|
682
|
+
lines.push("- `\u2191` / `\u2193` \u2014 recall previous prompts (when input is empty)");
|
|
683
|
+
lines.push("- `Enter` \u2014 send \xB7 `Shift+Enter` \u2014 newline");
|
|
684
|
+
lines.push("- `/` \u2014 open the slash-command menu (Tab / arrows to pick, Enter to insert)");
|
|
685
|
+
return lines.join("\n");
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/tool-formatters.ts
|
|
689
|
+
init_esm_shims();
|
|
690
|
+
|
|
691
|
+
// src/colors.ts
|
|
692
|
+
init_esm_shims();
|
|
693
|
+
var PROJECT_COLORS = [
|
|
694
|
+
"#60a5fa",
|
|
695
|
+
// blue
|
|
696
|
+
"#a78bfa",
|
|
697
|
+
// violet
|
|
698
|
+
"#4ade80",
|
|
699
|
+
// green
|
|
700
|
+
"#fbbf24",
|
|
701
|
+
// amber
|
|
702
|
+
"#f472b6",
|
|
703
|
+
// pink
|
|
704
|
+
"#22d3ee",
|
|
705
|
+
// cyan
|
|
706
|
+
"#fb923c",
|
|
707
|
+
// orange
|
|
708
|
+
"#34d399"
|
|
709
|
+
// emerald
|
|
710
|
+
];
|
|
711
|
+
function stableHash(s) {
|
|
712
|
+
let h = 0;
|
|
713
|
+
for (let i = 0; i < s.length; i++) {
|
|
714
|
+
h = h * 31 + s.charCodeAt(i) | 0;
|
|
715
|
+
}
|
|
716
|
+
return Math.abs(h);
|
|
717
|
+
}
|
|
718
|
+
function projectColor(name) {
|
|
719
|
+
return PROJECT_COLORS[stableHash(name) % PROJECT_COLORS.length];
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/tool-formatters.ts
|
|
723
|
+
function truncate(s, max) {
|
|
724
|
+
if (max <= 1) return "\u2026";
|
|
725
|
+
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
726
|
+
}
|
|
727
|
+
function promptWorkerDetailLen(project) {
|
|
728
|
+
const cols = process.stdout.columns ?? 80;
|
|
729
|
+
const fixed = 2 + 13 + 1 + project.length + 3 + 1 + 1 + 11 + 6;
|
|
730
|
+
return Math.max(20, cols - fixed);
|
|
731
|
+
}
|
|
732
|
+
var bossToolFormatters = {
|
|
733
|
+
formatLabel(name) {
|
|
734
|
+
switch (name) {
|
|
735
|
+
case "list_workers":
|
|
736
|
+
return "List Workers";
|
|
737
|
+
case "get_worker_status":
|
|
738
|
+
return "Worker Status";
|
|
739
|
+
case "prompt_worker":
|
|
740
|
+
return "Prompt Worker";
|
|
741
|
+
case "get_worker_summary":
|
|
742
|
+
return "Worker Summary";
|
|
743
|
+
default:
|
|
744
|
+
return void 0;
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
formatDetail(name, args) {
|
|
748
|
+
switch (name) {
|
|
749
|
+
case "list_workers":
|
|
750
|
+
return "";
|
|
751
|
+
case "get_worker_status":
|
|
752
|
+
case "get_worker_summary":
|
|
753
|
+
return truncate(String(args.project ?? ""), 40);
|
|
754
|
+
case "prompt_worker": {
|
|
755
|
+
const project = String(args.project ?? "");
|
|
756
|
+
const message = String(args.message ?? "").replace(/\s+/g, " ");
|
|
757
|
+
const fresh = args.fresh === true;
|
|
758
|
+
const maxMsg = promptWorkerDetailLen(project) - (fresh ? 8 : 0);
|
|
759
|
+
const truncMsg = truncate(message, Math.max(15, maxMsg));
|
|
760
|
+
const head = fresh ? "fresh \xB7 " : "";
|
|
761
|
+
return project ? `${head}${project} \xB7 ${truncMsg}` : `${head}${truncMsg}`;
|
|
762
|
+
}
|
|
763
|
+
default:
|
|
764
|
+
return void 0;
|
|
765
|
+
}
|
|
766
|
+
},
|
|
767
|
+
formatInline(name, result, isError) {
|
|
768
|
+
if (isError) return void 0;
|
|
769
|
+
switch (name) {
|
|
770
|
+
case "list_workers": {
|
|
771
|
+
const lines = result.split("\n").filter((l) => l.startsWith("-"));
|
|
772
|
+
return `${lines.length} worker${lines.length === 1 ? "" : "s"}`;
|
|
773
|
+
}
|
|
774
|
+
case "prompt_worker": {
|
|
775
|
+
if (result.includes("currently working")) {
|
|
776
|
+
return { text: "busy \u2014 skipped", color: "#fbbf24" };
|
|
42
777
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (!v)
|
|
46
|
-
throw new Error("--worker-model requires a value");
|
|
47
|
-
args.workerModel = v;
|
|
778
|
+
if (result.includes("Unknown project")) {
|
|
779
|
+
return { text: "unknown project", color: "#f87171" };
|
|
48
780
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
781
|
+
const project = String(result.match(/"([^"]+)"/)?.[1] ?? "");
|
|
782
|
+
const color = project ? projectColor(project) : "#e11d48";
|
|
783
|
+
return { text: "dispatched", color };
|
|
784
|
+
}
|
|
785
|
+
case "get_worker_status": {
|
|
786
|
+
const parts = result.split(":");
|
|
787
|
+
if (parts.length < 2) return void 0;
|
|
788
|
+
const status = parts.slice(1).join(":").trim();
|
|
789
|
+
const project = parts[0].trim();
|
|
790
|
+
return { text: status, color: projectColor(project) };
|
|
791
|
+
}
|
|
792
|
+
case "get_worker_summary": {
|
|
793
|
+
const turnMatch = result.match(/Turn:\s*(\d+)/);
|
|
794
|
+
const projectMatch = result.match(/Project:\s*(.+)/);
|
|
795
|
+
const toolsMatch = result.match(/Tools used:\s*(.+)/);
|
|
796
|
+
const tools = toolsMatch ? toolsMatch[1] : "";
|
|
797
|
+
const toolCount = tools && tools !== "(no tools used)" ? tools.split(",").length : 0;
|
|
798
|
+
const turn = turnMatch ? `turn ${turnMatch[1]}` : void 0;
|
|
799
|
+
const tCount = toolCount > 0 ? `${toolCount} tool${toolCount === 1 ? "" : "s"}` : void 0;
|
|
800
|
+
const summary = [turn, tCount].filter(Boolean).join(" \xB7 ");
|
|
801
|
+
if (!summary) return void 0;
|
|
802
|
+
const project = projectMatch ? projectMatch[1].trim() : "";
|
|
803
|
+
return project ? { text: summary, color: projectColor(project) } : { text: summary, color: "#9ca3af" };
|
|
804
|
+
}
|
|
805
|
+
default:
|
|
806
|
+
return void 0;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
// src/boss-phrases.ts
|
|
812
|
+
init_esm_shims();
|
|
813
|
+
var BOSS_PHRASES = {
|
|
814
|
+
// Generic between-states fallback. Probably never shown but keep for safety.
|
|
815
|
+
idle: ["Standing by", "Waiting for orders", "On call"],
|
|
816
|
+
// Boss has issued a request, waiting for the LLM to begin streaming.
|
|
817
|
+
waiting: [
|
|
818
|
+
"Briefing",
|
|
819
|
+
"Reviewing the room",
|
|
820
|
+
"Triaging",
|
|
821
|
+
"Lining up the brief",
|
|
822
|
+
"Surveying projects",
|
|
823
|
+
"Reading the room",
|
|
824
|
+
"Picking the right hand",
|
|
825
|
+
"Marshalling thoughts",
|
|
826
|
+
"Checking the board",
|
|
827
|
+
"Sizing up the work"
|
|
828
|
+
],
|
|
829
|
+
// LLM is mid-thinking-block (extended reasoning).
|
|
830
|
+
thinking: [
|
|
831
|
+
"Strategising",
|
|
832
|
+
"Plotting next move",
|
|
833
|
+
"Weighing options",
|
|
834
|
+
"Reasoning",
|
|
835
|
+
"Deliberating",
|
|
836
|
+
"Thinking it through",
|
|
837
|
+
"Mapping the play",
|
|
838
|
+
"Considering angles",
|
|
839
|
+
"Calculating odds",
|
|
840
|
+
"Drafting the call"
|
|
841
|
+
],
|
|
842
|
+
// LLM is streaming text — boss is forming its dispatch / response.
|
|
843
|
+
generating: [
|
|
844
|
+
"Drafting",
|
|
845
|
+
"Composing dispatch",
|
|
846
|
+
"Writing the brief",
|
|
847
|
+
"Penning instructions",
|
|
848
|
+
"Wording it up",
|
|
849
|
+
"Putting it on paper",
|
|
850
|
+
"Phrasing the ask",
|
|
851
|
+
"Forming the directive",
|
|
852
|
+
"Scripting the plan"
|
|
853
|
+
],
|
|
854
|
+
// Boss is invoking a tool — most often prompt_worker.
|
|
855
|
+
tools: [
|
|
856
|
+
"Coordinating",
|
|
857
|
+
"Dispatching",
|
|
858
|
+
"Routing",
|
|
859
|
+
"Delegating",
|
|
860
|
+
"Issuing orders",
|
|
861
|
+
"Handing off",
|
|
862
|
+
"Aligning workers",
|
|
863
|
+
"Conducting",
|
|
864
|
+
"Calling the team",
|
|
865
|
+
"Steering",
|
|
866
|
+
"Pulling levers"
|
|
867
|
+
],
|
|
868
|
+
// Provider retry (overloaded / rate-limited / etc.).
|
|
869
|
+
retrying: [
|
|
870
|
+
"Reattempting",
|
|
871
|
+
"Course correcting",
|
|
872
|
+
"Trying again",
|
|
873
|
+
"Pushing through",
|
|
874
|
+
"Holding the line"
|
|
875
|
+
]
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
// src/boss-tasks-overlay.tsx
|
|
879
|
+
init_esm_shims();
|
|
880
|
+
var import_react9 = __toESM(require_react(), 1);
|
|
881
|
+
var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
|
|
882
|
+
function statusGlyph(status) {
|
|
883
|
+
switch (status) {
|
|
884
|
+
case "done":
|
|
885
|
+
return "\u2713";
|
|
886
|
+
case "in_progress":
|
|
887
|
+
return "~";
|
|
888
|
+
case "blocked":
|
|
889
|
+
return "\u2717";
|
|
890
|
+
case "skipped":
|
|
891
|
+
return "\u2014";
|
|
892
|
+
default:
|
|
893
|
+
return " ";
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function BossTasksOverlay({
|
|
897
|
+
boss,
|
|
898
|
+
workers,
|
|
899
|
+
onClose
|
|
900
|
+
}) {
|
|
901
|
+
const theme = useTheme();
|
|
902
|
+
const tasksState = useTasksState();
|
|
903
|
+
const tasks = tasksState.tasks;
|
|
904
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react9.useState)(0);
|
|
905
|
+
const [status, setStatusMsg] = (0, import_react9.useState)("");
|
|
906
|
+
const statusTimer = (0, import_react9.useRef)(null);
|
|
907
|
+
const showStatus = (0, import_react9.useCallback)((msg) => {
|
|
908
|
+
setStatusMsg(msg);
|
|
909
|
+
if (statusTimer.current) clearTimeout(statusTimer.current);
|
|
910
|
+
statusTimer.current = setTimeout(() => setStatusMsg(""), 2500);
|
|
911
|
+
}, []);
|
|
912
|
+
const groupedTasks = workers.map((w) => ({
|
|
913
|
+
project: w.name,
|
|
914
|
+
tasks: tasks.filter((t) => t.project === w.name).sort((a, b) => a.createdAt.localeCompare(b.createdAt))
|
|
915
|
+
}));
|
|
916
|
+
const flatTasks = groupedTasks.flatMap((g) => g.tasks);
|
|
917
|
+
(0, import_react9.useEffect)(() => {
|
|
918
|
+
if (flatTasks.length === 0) {
|
|
919
|
+
setSelectedIndex(0);
|
|
920
|
+
} else if (selectedIndex >= flatTasks.length) {
|
|
921
|
+
setSelectedIndex(flatTasks.length - 1);
|
|
922
|
+
}
|
|
923
|
+
}, [flatTasks.length, selectedIndex]);
|
|
924
|
+
const selected = flatTasks[selectedIndex];
|
|
925
|
+
const MAX_VISIBLE = 12;
|
|
926
|
+
const startIdx = Math.max(
|
|
927
|
+
0,
|
|
928
|
+
Math.min(flatTasks.length - MAX_VISIBLE, selectedIndex - MAX_VISIBLE + 1, selectedIndex)
|
|
929
|
+
);
|
|
930
|
+
const endIdx = Math.min(flatTasks.length, startIdx + MAX_VISIBLE);
|
|
931
|
+
const visibleIdSet = new Set(flatTasks.slice(startIdx, endIdx).map((t) => t.id));
|
|
932
|
+
const showingTop = startIdx > 0;
|
|
933
|
+
const showingBottom = endIdx < flatTasks.length;
|
|
934
|
+
use_input_default((input, key) => {
|
|
935
|
+
if (key.escape) {
|
|
936
|
+
onClose();
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
if (key.upArrow || input === "k") {
|
|
940
|
+
setSelectedIndex((i) => Math.max(0, i - 1));
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
if (key.downArrow || input === "j") {
|
|
944
|
+
setSelectedIndex((i) => Math.min(flatTasks.length - 1, i + 1));
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
if (input === "d" && selected) {
|
|
948
|
+
void tasksStore.remove(selected.id).then(() => showStatus("Deleted"));
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if (input === "r") {
|
|
952
|
+
onClose();
|
|
953
|
+
void (async () => {
|
|
954
|
+
const dispatched = [];
|
|
955
|
+
for (const w of workers) {
|
|
956
|
+
const next = tasksStore.nextDispatchable(w.name);
|
|
957
|
+
if (!next) continue;
|
|
958
|
+
if (next.status === "blocked") {
|
|
959
|
+
await tasksStore.update(next.id, { status: "pending", notes: void 0 });
|
|
960
|
+
}
|
|
961
|
+
const res = await boss.dispatchTaskById(next.id);
|
|
962
|
+
if (res.ok) dispatched.push({ project: w.name, title: next.title });
|
|
54
963
|
}
|
|
55
|
-
|
|
56
|
-
|
|
964
|
+
if (dispatched.length === 0) {
|
|
965
|
+
bossStore.appendInfo("No pending or blocked tasks to run.", "info");
|
|
966
|
+
} else {
|
|
967
|
+
bossStore.appendTaskDispatch(dispatched);
|
|
57
968
|
}
|
|
58
|
-
|
|
59
|
-
|
|
969
|
+
})();
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
const doneCount = tasks.filter((t) => t.status === "done").length;
|
|
974
|
+
const inProgressCount = tasks.filter((t) => t.status === "in_progress").length;
|
|
975
|
+
const pendingCount = tasks.filter((t) => t.status === "pending").length;
|
|
976
|
+
const blockedCount = tasks.filter((t) => t.status === "blocked").length;
|
|
977
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, paddingX: 1, children: [
|
|
978
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
|
|
979
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: COLORS.primary, bold: true, children: "Tasks" }),
|
|
980
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${tasks.length} total \xB7 ` }),
|
|
981
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
982
|
+
CountsRow,
|
|
983
|
+
{
|
|
984
|
+
theme,
|
|
985
|
+
done: doneCount,
|
|
986
|
+
active: inProgressCount,
|
|
987
|
+
pending: pendingCount,
|
|
988
|
+
blocked: blockedCount
|
|
60
989
|
}
|
|
990
|
+
)
|
|
991
|
+
] }),
|
|
992
|
+
flatTasks.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.textDim, children: [
|
|
993
|
+
" No tasks yet. Ask the boss to plan some \u2014 e.g. ",
|
|
994
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.text, children: '"plan some work"' }),
|
|
995
|
+
"."
|
|
996
|
+
] }) }),
|
|
997
|
+
showingTop && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: ` \u2191 ${startIdx} more above` }),
|
|
998
|
+
groupedTasks.map((group, gIdx) => {
|
|
999
|
+
const startInFlat = groupedTasks.slice(0, gIdx).reduce((acc, g) => acc + g.tasks.length, 0);
|
|
1000
|
+
const visibleInSection = group.tasks.filter((t) => visibleIdSet.has(t.id));
|
|
1001
|
+
if (visibleInSection.length === 0) return null;
|
|
1002
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
|
|
1003
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
|
|
1004
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: projectColor(group.project), bold: true, children: group.project }),
|
|
1005
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${group.tasks.length}` })
|
|
1006
|
+
] }),
|
|
1007
|
+
visibleInSection.map((task) => {
|
|
1008
|
+
const realIdx = startInFlat + group.tasks.indexOf(task);
|
|
1009
|
+
const isSelected = realIdx === selectedIndex;
|
|
1010
|
+
const prefix = isSelected ? "\u276F " : " ";
|
|
1011
|
+
const glyph = statusGlyph(task.status);
|
|
1012
|
+
const color = isSelected ? theme.primary : task.status === "done" ? theme.success : task.status === "in_progress" ? theme.warning : task.status === "blocked" ? theme.error : theme.text;
|
|
1013
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color, bold: isSelected, children: [
|
|
1014
|
+
prefix,
|
|
1015
|
+
"[",
|
|
1016
|
+
glyph,
|
|
1017
|
+
"] ",
|
|
1018
|
+
task.title
|
|
1019
|
+
] }, task.id);
|
|
1020
|
+
})
|
|
1021
|
+
] }, group.project);
|
|
1022
|
+
}),
|
|
1023
|
+
showingBottom && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: ` \u2193 ${flatTasks.length - endIdx} more below` }),
|
|
1024
|
+
status && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.success, children: " " + status }) }),
|
|
1025
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.textDim, children: [
|
|
1026
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.primary, children: "\u2191\u2193" }),
|
|
1027
|
+
" move \xB7 (",
|
|
1028
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.primary, children: "d" }),
|
|
1029
|
+
")elete \xB7 (",
|
|
1030
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.primary, children: "r" }),
|
|
1031
|
+
")un pending \xB7 ",
|
|
1032
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.primary, children: "ESC" }),
|
|
1033
|
+
" close"
|
|
1034
|
+
] }) })
|
|
1035
|
+
] });
|
|
1036
|
+
}
|
|
1037
|
+
function CountsRow({
|
|
1038
|
+
theme,
|
|
1039
|
+
done,
|
|
1040
|
+
active,
|
|
1041
|
+
pending,
|
|
1042
|
+
blocked
|
|
1043
|
+
}) {
|
|
1044
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
|
|
1045
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.success, children: [
|
|
1046
|
+
done,
|
|
1047
|
+
" done"
|
|
1048
|
+
] }),
|
|
1049
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
|
|
1050
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.warning, children: [
|
|
1051
|
+
active,
|
|
1052
|
+
" active"
|
|
1053
|
+
] }),
|
|
1054
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
|
|
1055
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.text, children: [
|
|
1056
|
+
pending,
|
|
1057
|
+
" pending"
|
|
1058
|
+
] }),
|
|
1059
|
+
blocked > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
1060
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
|
|
1061
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.error, children: [
|
|
1062
|
+
blocked,
|
|
1063
|
+
" blocked"
|
|
1064
|
+
] })
|
|
1065
|
+
] })
|
|
1066
|
+
] });
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// src/radio-picker.tsx
|
|
1070
|
+
init_esm_shims();
|
|
1071
|
+
var import_react10 = __toESM(require_react(), 1);
|
|
1072
|
+
|
|
1073
|
+
// src/radio.ts
|
|
1074
|
+
init_esm_shims();
|
|
1075
|
+
import { spawn } from "child_process";
|
|
1076
|
+
var RADIO_STATIONS = [
|
|
1077
|
+
{
|
|
1078
|
+
id: "somafm-groove-salad",
|
|
1079
|
+
name: "SomaFM \xB7 Groove Salad",
|
|
1080
|
+
description: "Chilled downtempo, ambient grooves",
|
|
1081
|
+
url: "http://ice1.somafm.com/groovesalad-128-mp3"
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
id: "somafm-drone-zone",
|
|
1085
|
+
name: "SomaFM \xB7 Drone Zone",
|
|
1086
|
+
description: "Atmospheric textures with minimal beats",
|
|
1087
|
+
url: "http://ice1.somafm.com/dronezone-128-mp3"
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
id: "radio-paradise",
|
|
1091
|
+
name: "Radio Paradise",
|
|
1092
|
+
description: "Eclectic mix \u2014 rock, electronica, jazz",
|
|
1093
|
+
url: "http://stream.radioparadise.com/mp3-128"
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
id: "george-fm",
|
|
1097
|
+
name: "George FM",
|
|
1098
|
+
description: "NZ dance + electronic",
|
|
1099
|
+
url: "https://mediaworks.streamguys1.com/george_net_icy"
|
|
1100
|
+
}
|
|
1101
|
+
];
|
|
1102
|
+
var PLAYERS = [
|
|
1103
|
+
{ cmd: "mpv", args: (u) => ["--really-quiet", "--no-video", "--no-terminal", u] },
|
|
1104
|
+
{
|
|
1105
|
+
cmd: "ffplay",
|
|
1106
|
+
args: (u) => ["-nodisp", "-autoexit", "-loglevel", "quiet", u]
|
|
1107
|
+
},
|
|
1108
|
+
{ cmd: "mpg123", args: (u) => ["-q", u] },
|
|
1109
|
+
{ cmd: "cvlc", args: (u) => ["--play-and-exit", "--quiet", u] }
|
|
1110
|
+
];
|
|
1111
|
+
var currentChild = null;
|
|
1112
|
+
var currentStationId = null;
|
|
1113
|
+
function getCurrentStation() {
|
|
1114
|
+
return currentStationId;
|
|
1115
|
+
}
|
|
1116
|
+
function stopRadio() {
|
|
1117
|
+
if (!currentChild) return;
|
|
1118
|
+
try {
|
|
1119
|
+
if (process.platform !== "win32" && currentChild.pid) {
|
|
1120
|
+
try {
|
|
1121
|
+
process.kill(-currentChild.pid, "SIGTERM");
|
|
1122
|
+
} catch {
|
|
1123
|
+
currentChild.kill("SIGTERM");
|
|
1124
|
+
}
|
|
1125
|
+
} else {
|
|
1126
|
+
currentChild.kill("SIGTERM");
|
|
61
1127
|
}
|
|
62
|
-
|
|
1128
|
+
} catch {
|
|
1129
|
+
}
|
|
1130
|
+
currentChild = null;
|
|
1131
|
+
currentStationId = null;
|
|
1132
|
+
log("INFO", "radio", "stopped");
|
|
63
1133
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
c(COLORS.textDim, " show this help\n\n") +
|
|
98
|
-
c(COLORS.textDim, "Talk to the boss at the prompt. Press ") +
|
|
99
|
-
c(COLORS.accent, "Ctrl+C") +
|
|
100
|
-
c(COLORS.textDim, " twice to exit.\n\n"));
|
|
101
|
-
process.exit(0);
|
|
1134
|
+
function playRadio(stationId) {
|
|
1135
|
+
const station = RADIO_STATIONS.find((s) => s.id === stationId);
|
|
1136
|
+
if (!station) return { ok: false, error: `Unknown station: ${stationId}` };
|
|
1137
|
+
stopRadio();
|
|
1138
|
+
for (const player of PLAYERS) {
|
|
1139
|
+
try {
|
|
1140
|
+
const child = spawn(player.cmd, player.args(station.url), {
|
|
1141
|
+
detached: process.platform !== "win32",
|
|
1142
|
+
stdio: "ignore"
|
|
1143
|
+
});
|
|
1144
|
+
let errored = false;
|
|
1145
|
+
child.once("error", () => {
|
|
1146
|
+
errored = true;
|
|
1147
|
+
});
|
|
1148
|
+
if (child.pid && !errored) {
|
|
1149
|
+
currentChild = child;
|
|
1150
|
+
currentStationId = stationId;
|
|
1151
|
+
log("INFO", "radio", "playing", {
|
|
1152
|
+
station: station.id,
|
|
1153
|
+
player: player.cmd,
|
|
1154
|
+
url: station.url
|
|
1155
|
+
});
|
|
1156
|
+
child.unref();
|
|
1157
|
+
return { ok: true };
|
|
1158
|
+
}
|
|
1159
|
+
} catch {
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
log("WARN", "radio", "no compatible player found", { platform: process.platform });
|
|
1163
|
+
return {
|
|
1164
|
+
ok: false,
|
|
1165
|
+
error: buildInstallHint()
|
|
1166
|
+
};
|
|
102
1167
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
1168
|
+
function buildInstallHint() {
|
|
1169
|
+
const base = "Radio needs a streaming player. Install one of: mpv (recommended), ffplay, mpg123, or vlc.";
|
|
1170
|
+
switch (process.platform) {
|
|
1171
|
+
case "darwin":
|
|
1172
|
+
return `${base} On macOS: \`brew install mpv\` (or \`brew install ffmpeg\` for ffplay).`;
|
|
1173
|
+
case "linux":
|
|
1174
|
+
return `${base} On Linux (Debian/Ubuntu): \`sudo apt install mpv\`. Fedora: \`sudo dnf install mpv\`. Arch: \`sudo pacman -S mpv\`.`;
|
|
1175
|
+
case "win32":
|
|
1176
|
+
return `${base} On Windows: \`winget install mpv.mpv\` (or download from https://mpv.io).`;
|
|
1177
|
+
default:
|
|
1178
|
+
return `${base} See https://mpv.io for platform installation instructions.`;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/radio-picker.tsx
|
|
1183
|
+
var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
|
|
1184
|
+
function RadioPicker({
|
|
1185
|
+
currentStationId: currentStationId2,
|
|
1186
|
+
onSelect,
|
|
1187
|
+
onCancel
|
|
1188
|
+
}) {
|
|
1189
|
+
const items = [
|
|
1190
|
+
...RADIO_STATIONS.map((s) => ({
|
|
1191
|
+
label: `${currentStationId2 === s.id ? "* " : " "}${s.name}`,
|
|
1192
|
+
value: s.id,
|
|
1193
|
+
description: s.description
|
|
1194
|
+
})),
|
|
1195
|
+
{
|
|
1196
|
+
label: `${currentStationId2 === null ? "* " : " "}Off`,
|
|
1197
|
+
value: "off",
|
|
1198
|
+
description: "Stop the radio"
|
|
1199
|
+
}
|
|
1200
|
+
];
|
|
1201
|
+
const initialIndex = Math.max(
|
|
1202
|
+
0,
|
|
1203
|
+
items.findIndex((i) => i.value === (currentStationId2 ?? "off"))
|
|
1204
|
+
);
|
|
1205
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1206
|
+
SelectList,
|
|
1207
|
+
{
|
|
1208
|
+
items,
|
|
1209
|
+
onSelect: (v) => onSelect(v === "off" ? "off" : v),
|
|
1210
|
+
onCancel,
|
|
1211
|
+
initialIndex,
|
|
1212
|
+
windowSize: 6
|
|
1213
|
+
}
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// src/auto-update.ts
|
|
1218
|
+
init_esm_shims();
|
|
1219
|
+
import { spawn as spawn2 } from "child_process";
|
|
1220
|
+
import fs3 from "fs";
|
|
1221
|
+
import path3 from "path";
|
|
1222
|
+
import os from "os";
|
|
1223
|
+
var PACKAGE_NAME = "@kenkaiiii/gg-boss";
|
|
1224
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
1225
|
+
var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
1226
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
1227
|
+
function getStateFilePath() {
|
|
1228
|
+
return path3.join(os.homedir(), ".gg", "boss", "update-state.json");
|
|
1229
|
+
}
|
|
1230
|
+
function readState() {
|
|
1231
|
+
try {
|
|
1232
|
+
const raw = fs3.readFileSync(getStateFilePath(), "utf-8");
|
|
1233
|
+
return JSON.parse(raw);
|
|
1234
|
+
} catch {
|
|
1235
|
+
return null;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
function writeState(state) {
|
|
1239
|
+
try {
|
|
1240
|
+
const dir = path3.dirname(getStateFilePath());
|
|
1241
|
+
fs3.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1242
|
+
fs3.writeFileSync(getStateFilePath(), JSON.stringify(state));
|
|
1243
|
+
} catch {
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
function compareVersions(a, b) {
|
|
1247
|
+
const pa = a.split(".").map(Number);
|
|
1248
|
+
const pb = b.split(".").map(Number);
|
|
1249
|
+
for (let i = 0; i < 3; i++) {
|
|
1250
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1251
|
+
if (diff !== 0) return diff;
|
|
1252
|
+
}
|
|
1253
|
+
return 0;
|
|
1254
|
+
}
|
|
1255
|
+
function detectInstallInfo() {
|
|
1256
|
+
const scriptPath = (process.argv[1] ?? "").replace(/\\/g, "/");
|
|
1257
|
+
if (scriptPath.includes("/_npx/")) {
|
|
1258
|
+
return { packageManager: "unknown" /* UNKNOWN */, updateCommand: null };
|
|
1259
|
+
}
|
|
1260
|
+
if (scriptPath.includes("/.pnpm") || scriptPath.includes("/pnpm/global")) {
|
|
1261
|
+
return {
|
|
1262
|
+
packageManager: "pnpm" /* PNPM */,
|
|
1263
|
+
updateCommand: `pnpm add -g ${PACKAGE_NAME}@latest`
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
if (scriptPath.includes("/.yarn/") || scriptPath.includes("/yarn/global")) {
|
|
1267
|
+
return {
|
|
1268
|
+
packageManager: "yarn" /* YARN */,
|
|
1269
|
+
updateCommand: `yarn global add ${PACKAGE_NAME}@latest`
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
return {
|
|
1273
|
+
packageManager: "npm" /* NPM */,
|
|
1274
|
+
updateCommand: `npm install -g ${PACKAGE_NAME}@latest`
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
async function fetchLatestVersion() {
|
|
1278
|
+
try {
|
|
1279
|
+
const controller = new AbortController();
|
|
1280
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
1281
|
+
const response = await fetch(REGISTRY_URL, { signal: controller.signal });
|
|
1282
|
+
clearTimeout(timeout);
|
|
1283
|
+
const data = await response.json();
|
|
1284
|
+
const version = data.version?.trim();
|
|
1285
|
+
return version && /^\d+\.\d+\.\d+/.test(version) ? version : null;
|
|
1286
|
+
} catch {
|
|
1287
|
+
return null;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
function performUpdateInBackground(command) {
|
|
1291
|
+
try {
|
|
1292
|
+
const parts = command.split(" ");
|
|
1293
|
+
const child = spawn2(parts[0], parts.slice(1), {
|
|
1294
|
+
detached: true,
|
|
1295
|
+
stdio: "ignore",
|
|
1296
|
+
env: { ...process.env, npm_config_loglevel: "silent" }
|
|
125
1297
|
});
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
1298
|
+
child.unref();
|
|
1299
|
+
} catch {
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function checkAndAutoUpdate(currentVersion) {
|
|
1303
|
+
try {
|
|
1304
|
+
const state = readState();
|
|
1305
|
+
let message = null;
|
|
1306
|
+
if (state?.updatePending && state.latestVersion) {
|
|
1307
|
+
if (compareVersions(state.latestVersion, currentVersion) > 0) {
|
|
1308
|
+
const info = detectInstallInfo();
|
|
1309
|
+
if (info.updateCommand) {
|
|
1310
|
+
performUpdateInBackground(info.updateCommand);
|
|
1311
|
+
message = `Ken just shipped ${state.latestVersion}! Installing in the background \u2014 takes effect next launch.`;
|
|
1312
|
+
writeState({
|
|
1313
|
+
...state,
|
|
1314
|
+
lastCheckedAt: Date.now(),
|
|
1315
|
+
updatePending: false,
|
|
1316
|
+
lastUpdateAttempt: Date.now()
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
} else {
|
|
1320
|
+
writeState({ ...state, updatePending: false });
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
const shouldCheck = !state || Date.now() - state.lastCheckedAt > CHECK_INTERVAL_MS;
|
|
1324
|
+
if (shouldCheck) scheduleBackgroundCheck(currentVersion);
|
|
1325
|
+
return message;
|
|
1326
|
+
} catch {
|
|
1327
|
+
return null;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function getPendingUpdate(currentVersion) {
|
|
1331
|
+
try {
|
|
1332
|
+
const state = readState();
|
|
1333
|
+
if (!state?.latestVersion) return null;
|
|
1334
|
+
if (compareVersions(state.latestVersion, currentVersion) <= 0) return null;
|
|
1335
|
+
return { latestVersion: state.latestVersion };
|
|
1336
|
+
} catch {
|
|
1337
|
+
return null;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
function scheduleBackgroundCheck(currentVersion) {
|
|
1341
|
+
fetchLatestVersion().then((latestVersion) => {
|
|
1342
|
+
const newState = {
|
|
1343
|
+
lastCheckedAt: Date.now(),
|
|
1344
|
+
latestVersion: latestVersion ?? void 0,
|
|
1345
|
+
updatePending: false
|
|
1346
|
+
};
|
|
1347
|
+
if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
|
|
1348
|
+
newState.updatePending = true;
|
|
1349
|
+
}
|
|
1350
|
+
writeState(newState);
|
|
1351
|
+
}).catch(() => {
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
var periodicTimer = null;
|
|
1355
|
+
function startPeriodicUpdateCheck(currentVersion, onUpdate) {
|
|
1356
|
+
if (periodicTimer) return;
|
|
1357
|
+
periodicTimer = setInterval(() => {
|
|
1358
|
+
fetchLatestVersion().then((latestVersion) => {
|
|
1359
|
+
if (!latestVersion) return;
|
|
1360
|
+
if (compareVersions(latestVersion, currentVersion) <= 0) return;
|
|
1361
|
+
const info = detectInstallInfo();
|
|
1362
|
+
if (!info.updateCommand) return;
|
|
1363
|
+
writeState({
|
|
1364
|
+
lastCheckedAt: Date.now(),
|
|
1365
|
+
latestVersion,
|
|
1366
|
+
updatePending: true
|
|
1367
|
+
});
|
|
1368
|
+
onUpdate(
|
|
1369
|
+
`Ken just pushed a fresh update \u2014 ${currentVersion} \u2192 ${latestVersion}! Restart ggboss to grab it (or run ${info.updateCommand} if you can't wait).`
|
|
1370
|
+
);
|
|
1371
|
+
stopPeriodicUpdateCheck();
|
|
1372
|
+
}).catch(() => {
|
|
144
1373
|
});
|
|
145
|
-
|
|
146
|
-
|
|
1374
|
+
}, CHECK_INTERVAL_MS);
|
|
1375
|
+
periodicTimer.unref();
|
|
1376
|
+
}
|
|
1377
|
+
function stopPeriodicUpdateCheck() {
|
|
1378
|
+
if (periodicTimer) {
|
|
1379
|
+
clearInterval(periodicTimer);
|
|
1380
|
+
periodicTimer = null;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// src/orchestrator-app.tsx
|
|
1385
|
+
var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
|
|
1386
|
+
function BossApp({ boss }) {
|
|
1387
|
+
const theme = loadTheme("dark");
|
|
1388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TerminalSizeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AnimationProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BossAppInner, { boss }) }) }) });
|
|
1389
|
+
}
|
|
1390
|
+
function BossAppInner({ boss }) {
|
|
1391
|
+
const state = useBossState();
|
|
1392
|
+
const { exit } = use_app_default();
|
|
1393
|
+
const { stdout } = use_stdout_default();
|
|
1394
|
+
const { resizeKey, columns } = useTerminalSize();
|
|
1395
|
+
const runStartRef = (0, import_react11.useRef)(null);
|
|
1396
|
+
runStartRef.current = state.runStartMs;
|
|
1397
|
+
const charCountRef = (0, import_react11.useRef)(0);
|
|
1398
|
+
charCountRef.current = state.streaming?.text.length ?? 0;
|
|
1399
|
+
const realTokensAccumRef = (0, import_react11.useRef)(0);
|
|
1400
|
+
realTokensAccumRef.current = state.bossInputTokens;
|
|
1401
|
+
const [lastUserMessage, setLastUserMessage] = (0, import_react11.useState)("");
|
|
1402
|
+
const [overlay, setOverlay] = (0, import_react11.useState)(
|
|
1403
|
+
null
|
|
1404
|
+
);
|
|
1405
|
+
const [currentRadio, setCurrentRadio] = (0, import_react11.useState)(() => getCurrentStation());
|
|
1406
|
+
const [staticKey, setStaticKey] = (0, import_react11.useState)(0);
|
|
1407
|
+
const [updatePending, setUpdatePending] = (0, import_react11.useState)(
|
|
1408
|
+
() => getPendingUpdate(VERSION) !== null
|
|
1409
|
+
);
|
|
1410
|
+
(0, import_react11.useEffect)(() => {
|
|
1411
|
+
startPeriodicUpdateCheck(VERSION, (msg) => {
|
|
1412
|
+
bossStore.appendInfo(msg, "info");
|
|
1413
|
+
setUpdatePending(true);
|
|
147
1414
|
});
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
1415
|
+
return () => stopPeriodicUpdateCheck();
|
|
1416
|
+
}, []);
|
|
1417
|
+
const workersRunning = state.workers.filter((w) => w.status === "working").length;
|
|
1418
|
+
const titlePrevRef = (0, import_react11.useRef)("");
|
|
1419
|
+
(0, import_react11.useEffect)(() => {
|
|
1420
|
+
if (!stdout) return;
|
|
1421
|
+
let title;
|
|
1422
|
+
if (workersRunning > 0) {
|
|
1423
|
+
const label = `${workersRunning} worker${workersRunning === 1 ? "" : "s"} running`;
|
|
1424
|
+
title = `\u25CF ${label} \xB7 GG Boss`;
|
|
1425
|
+
} else if (state.phase === "working") {
|
|
1426
|
+
title = "\u25CF GG Boss";
|
|
1427
|
+
} else {
|
|
1428
|
+
title = "GG Boss";
|
|
1429
|
+
}
|
|
1430
|
+
if (title !== titlePrevRef.current) {
|
|
1431
|
+
titlePrevRef.current = title;
|
|
1432
|
+
stdout.write(`\x1B]0;${title}\x1B\\`);
|
|
1433
|
+
}
|
|
1434
|
+
}, [stdout, workersRunning, state.phase]);
|
|
1435
|
+
(0, import_react11.useEffect)(() => {
|
|
1436
|
+
return () => {
|
|
1437
|
+
stdout?.write(`\x1B]0;GG Boss\x1B\\`);
|
|
1438
|
+
};
|
|
1439
|
+
}, [stdout]);
|
|
1440
|
+
const staticItems = (0, import_react11.useMemo)(
|
|
1441
|
+
() => [{ kind: "banner", id: "banner" }, ...state.history],
|
|
1442
|
+
[state.history]
|
|
1443
|
+
);
|
|
1444
|
+
const openOverlay = (0, import_react11.useCallback)(
|
|
1445
|
+
(next) => {
|
|
1446
|
+
setOverlay(next);
|
|
1447
|
+
},
|
|
1448
|
+
[]
|
|
1449
|
+
);
|
|
1450
|
+
const closeOverlay = (0, import_react11.useCallback)(() => {
|
|
1451
|
+
setOverlay(null);
|
|
1452
|
+
}, []);
|
|
1453
|
+
void stdout;
|
|
1454
|
+
const handleDoubleExit = useDoublePress(
|
|
1455
|
+
(pending) => bossStore.setExitPending(pending),
|
|
1456
|
+
() => exit()
|
|
1457
|
+
);
|
|
1458
|
+
(0, import_react11.useEffect)(() => {
|
|
1459
|
+
if (state.pendingFlush.length > 0) {
|
|
1460
|
+
bossStore.commitPendingFlush();
|
|
1461
|
+
}
|
|
1462
|
+
}, [state.flushGeneration, state.pendingFlush.length]);
|
|
1463
|
+
use_input_default((input, key) => {
|
|
1464
|
+
if (key.ctrl && input === "t") {
|
|
1465
|
+
if (overlay === "tasks") closeOverlay();
|
|
1466
|
+
else openOverlay("tasks");
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
if (key.escape && state.phase === "working") {
|
|
1470
|
+
boss.abort();
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
const handleSlashCommand = async (value) => {
|
|
1474
|
+
const parsed = parseSlash(value);
|
|
1475
|
+
if (!parsed) return false;
|
|
1476
|
+
const name = canonicalName(parsed.name);
|
|
1477
|
+
if (!name) {
|
|
1478
|
+
bossStore.appendInfo(`Unknown command: /${parsed.name}`, "warning");
|
|
1479
|
+
return true;
|
|
1480
|
+
}
|
|
1481
|
+
switch (name) {
|
|
1482
|
+
case "help":
|
|
1483
|
+
bossStore.appendUser(value);
|
|
1484
|
+
bossStore.appendInfo(buildHelpText(), "info");
|
|
1485
|
+
return true;
|
|
1486
|
+
case "clear":
|
|
1487
|
+
stdout?.write("\x1B[2J\x1B[3J\x1B[H");
|
|
1488
|
+
bossStore.clearHistory();
|
|
1489
|
+
await boss.resetConversation();
|
|
1490
|
+
setStaticKey((k) => k + 1);
|
|
1491
|
+
bossStore.appendInfo("Session cleared.", "info");
|
|
1492
|
+
return true;
|
|
1493
|
+
case "model-boss":
|
|
1494
|
+
openOverlay("model-boss");
|
|
1495
|
+
return true;
|
|
1496
|
+
case "model-workers":
|
|
1497
|
+
openOverlay("model-workers");
|
|
1498
|
+
return true;
|
|
1499
|
+
case "compact":
|
|
1500
|
+
bossStore.appendUser(value);
|
|
1501
|
+
await boss.manualCompact();
|
|
1502
|
+
return true;
|
|
1503
|
+
case "radio":
|
|
1504
|
+
openOverlay("radio");
|
|
1505
|
+
return true;
|
|
1506
|
+
case "quit":
|
|
1507
|
+
exit();
|
|
1508
|
+
return true;
|
|
1509
|
+
}
|
|
1510
|
+
return false;
|
|
1511
|
+
};
|
|
1512
|
+
const handleModelSelect = (value) => {
|
|
1513
|
+
const colon = value.indexOf(":");
|
|
1514
|
+
if (colon < 0) {
|
|
1515
|
+
closeOverlay();
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
const provider = value.slice(0, colon);
|
|
1519
|
+
const model = value.slice(colon + 1);
|
|
1520
|
+
if (overlay === "model-boss") {
|
|
1521
|
+
void boss.switchBossModel(provider, model);
|
|
1522
|
+
} else if (overlay === "model-workers") {
|
|
1523
|
+
void boss.switchWorkerModel(provider, model);
|
|
1524
|
+
}
|
|
1525
|
+
closeOverlay();
|
|
1526
|
+
};
|
|
1527
|
+
const handleSubmit = (value) => {
|
|
1528
|
+
const trimmed = value.trim();
|
|
1529
|
+
if (!trimmed) return;
|
|
1530
|
+
if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
|
|
1531
|
+
void handleSlashCommand(trimmed);
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
bossStore.appendUser(trimmed);
|
|
1535
|
+
setLastUserMessage(trimmed);
|
|
1536
|
+
const scoped = scopePrefix(state.scope) + trimmed;
|
|
1537
|
+
boss.enqueueUserMessage(scoped);
|
|
1538
|
+
};
|
|
1539
|
+
const handleAbort = () => {
|
|
1540
|
+
if (state.phase === "working") {
|
|
1541
|
+
boss.abort();
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
handleDoubleExit();
|
|
1545
|
+
};
|
|
1546
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", width: columns, children: [
|
|
1547
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Static, { items: staticItems, style: { width: "100%" }, children: (item) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexDirection: "column", paddingRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StaticRowView, { row: item }) }, item.id) }, `${resizeKey}-${staticKey}`),
|
|
1548
|
+
overlay === "tasks" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BossTasksOverlay, { boss, workers: state.workers, onClose: closeOverlay }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1549
|
+
state.streaming && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StreamingTurnView, { turn: state.streaming, isRunning: state.phase === "working" }),
|
|
1550
|
+
state.phase === "working" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1551
|
+
ActivityIndicator,
|
|
1552
|
+
{
|
|
1553
|
+
phase: state.activityPhase,
|
|
1554
|
+
elapsedMs: state.runStartMs ? Date.now() - state.runStartMs : 0,
|
|
1555
|
+
runStartRef,
|
|
1556
|
+
thinkingMs: state.streaming?.thinkingMs ?? 0,
|
|
1557
|
+
isThinking: state.activityPhase === "thinking",
|
|
1558
|
+
tokenEstimate: state.bossInputTokens,
|
|
1559
|
+
charCountRef,
|
|
1560
|
+
realTokensAccumRef,
|
|
1561
|
+
userMessage: lastUserMessage,
|
|
1562
|
+
activeToolNames: (state.streaming?.tools ?? []).filter((t) => t.status === "running").map((t) => t.name),
|
|
1563
|
+
retryInfo: state.retryInfo,
|
|
1564
|
+
phrases: BOSS_PHRASES,
|
|
1565
|
+
pulseColors: PULSE_COLORS
|
|
1566
|
+
}
|
|
1567
|
+
) }),
|
|
1568
|
+
state.compaction?.state === "running" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CompactionSpinner, {}),
|
|
1569
|
+
state.compaction?.state === "done" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1570
|
+
CompactionDone,
|
|
1571
|
+
{
|
|
1572
|
+
originalCount: state.compaction.originalCount,
|
|
1573
|
+
newCount: state.compaction.newCount,
|
|
1574
|
+
tokensBefore: state.compaction.tokensBefore,
|
|
1575
|
+
tokensAfter: state.compaction.tokensAfter
|
|
1576
|
+
}
|
|
1577
|
+
),
|
|
1578
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1579
|
+
InputArea,
|
|
1580
|
+
{
|
|
1581
|
+
onSubmit: handleSubmit,
|
|
1582
|
+
onAbort: handleAbort,
|
|
1583
|
+
disabled: state.phase === "working",
|
|
1584
|
+
isActive: !overlay,
|
|
1585
|
+
cwd: process.cwd(),
|
|
1586
|
+
commands: BOSS_SLASH_COMMANDS,
|
|
1587
|
+
scopeBadge: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScopePill, { scope: state.scope }),
|
|
1588
|
+
disableMouseTracking: true,
|
|
1589
|
+
onTab: () => bossStore.cycleScope(),
|
|
1590
|
+
onShiftTab: () => {
|
|
1591
|
+
const next = state.bossThinkingLevel ? void 0 : "medium";
|
|
1592
|
+
void boss.setBossThinking(next);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
),
|
|
1596
|
+
overlay === "model-boss" || overlay === "model-workers" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1597
|
+
ModelSelector,
|
|
1598
|
+
{
|
|
1599
|
+
onSelect: handleModelSelect,
|
|
1600
|
+
onCancel: closeOverlay,
|
|
1601
|
+
loggedInProviders: state.loggedInProviders,
|
|
1602
|
+
currentModel: overlay === "model-boss" ? state.bossModel : state.workerModel,
|
|
1603
|
+
currentProvider: overlay === "model-boss" ? state.bossProvider : state.workerProvider
|
|
1604
|
+
}
|
|
1605
|
+
) : overlay === "radio" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1606
|
+
RadioPicker,
|
|
1607
|
+
{
|
|
1608
|
+
currentStationId: currentRadio,
|
|
1609
|
+
onCancel: closeOverlay,
|
|
1610
|
+
onSelect: (value) => {
|
|
1611
|
+
if (value === "off") {
|
|
1612
|
+
stopRadio();
|
|
1613
|
+
setCurrentRadio(null);
|
|
1614
|
+
bossStore.appendInfo("Radio off.", "info");
|
|
1615
|
+
} else {
|
|
1616
|
+
const result = playRadio(value);
|
|
1617
|
+
if (result.ok) {
|
|
1618
|
+
setCurrentRadio(value);
|
|
1619
|
+
const station = RADIO_STATIONS.find((s) => s.id === value);
|
|
1620
|
+
bossStore.appendInfo(`Now playing: ${station?.name ?? value}`, "info");
|
|
1621
|
+
} else {
|
|
1622
|
+
bossStore.appendInfo(result.error ?? "Radio failed to start.", "warning");
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
closeOverlay();
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1629
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1630
|
+
BossFooter,
|
|
1631
|
+
{
|
|
1632
|
+
bossModel: state.bossModel,
|
|
1633
|
+
workerModel: state.workerModel,
|
|
1634
|
+
tokensIn: state.bossInputTokens,
|
|
1635
|
+
exitPending: state.exitPending,
|
|
1636
|
+
bossThinkingLevel: state.bossThinkingLevel,
|
|
1637
|
+
updatePending,
|
|
1638
|
+
currentRadioStationId: currentRadio
|
|
1639
|
+
}
|
|
1640
|
+
),
|
|
1641
|
+
!state.exitPending && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1642
|
+
WorkerStatusBar,
|
|
1643
|
+
{
|
|
1644
|
+
workers: state.workers,
|
|
1645
|
+
pendingMessages: state.pendingUserMessages
|
|
1646
|
+
}
|
|
1647
|
+
)
|
|
1648
|
+
] })
|
|
1649
|
+
] })
|
|
1650
|
+
] });
|
|
1651
|
+
}
|
|
1652
|
+
function ScopePill({ scope }) {
|
|
1653
|
+
const theme = useTheme();
|
|
1654
|
+
const isAll = scope === "all";
|
|
1655
|
+
const bg = isAll ? COLORS.accent : projectColor(scope);
|
|
1656
|
+
const label = isAll ? "All" : scope;
|
|
1657
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
|
|
1658
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.textDim, children: "Project " }),
|
|
1659
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "black", backgroundColor: bg, bold: true, children: ` ${label} ` }),
|
|
1660
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.textDim, children: [
|
|
1661
|
+
" ",
|
|
1662
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.primary, children: "Tab" }),
|
|
1663
|
+
" to switch"
|
|
1664
|
+
] })
|
|
1665
|
+
] });
|
|
1666
|
+
}
|
|
1667
|
+
function scopePrefix(scope) {
|
|
1668
|
+
if (scope === "all") return "[scope:all] ";
|
|
1669
|
+
return `[scope:${scope}] `;
|
|
1670
|
+
}
|
|
1671
|
+
var SHIMMER_WIDTH = 3;
|
|
1672
|
+
function formatElapsed(ms) {
|
|
1673
|
+
const total = Math.floor(ms / 1e3);
|
|
1674
|
+
const m = Math.floor(total / 60);
|
|
1675
|
+
const s = total % 60;
|
|
1676
|
+
return `${m}:${s.toString().padStart(2, "0")}`;
|
|
1677
|
+
}
|
|
1678
|
+
function AnimationActiveSentinel() {
|
|
1679
|
+
useAnimationActive();
|
|
1680
|
+
return null;
|
|
1681
|
+
}
|
|
1682
|
+
function ShimmerName({
|
|
1683
|
+
name,
|
|
1684
|
+
color,
|
|
1685
|
+
tick
|
|
1686
|
+
}) {
|
|
1687
|
+
const cycle = name.length + SHIMMER_WIDTH * 2;
|
|
1688
|
+
const shimmerPos = tick % cycle - SHIMMER_WIDTH;
|
|
1689
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: name.split("").map((ch, i) => {
|
|
1690
|
+
const isBright = Math.abs(i - shimmerPos) <= SHIMMER_WIDTH;
|
|
1691
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color, bold: isBright, dimColor: !isBright, children: ch }, i);
|
|
1692
|
+
}) });
|
|
1693
|
+
}
|
|
1694
|
+
function WorkerStatusBar({
|
|
1695
|
+
workers,
|
|
1696
|
+
pendingMessages
|
|
1697
|
+
}) {
|
|
1698
|
+
const theme = useTheme();
|
|
1699
|
+
const working = workers.filter((w) => w.status === "working");
|
|
1700
|
+
const errored = workers.filter((w) => w.status === "error");
|
|
1701
|
+
const idleCount = workers.length - working.length - errored.length;
|
|
1702
|
+
const anyWorking = working.length > 0;
|
|
1703
|
+
const tick = useAnimationTick();
|
|
1704
|
+
const now = Date.now();
|
|
1705
|
+
if (workers.length === 0) return null;
|
|
1706
|
+
const slots = [];
|
|
1707
|
+
for (const w of working) {
|
|
1708
|
+
const projectHue = projectColor(w.name);
|
|
1709
|
+
const elapsed = w.workStartedAt ? formatElapsed(now - w.workStartedAt) : null;
|
|
1710
|
+
slots.push(
|
|
1711
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react11.default.Fragment, { children: [
|
|
1712
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ShimmerName, { name: w.name, color: projectHue, tick }),
|
|
1713
|
+
elapsed && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.textDim, children: [
|
|
1714
|
+
" ",
|
|
1715
|
+
elapsed
|
|
1716
|
+
] })
|
|
1717
|
+
] }, `w-${w.name}`)
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
for (const w of errored) {
|
|
1721
|
+
slots.push(
|
|
1722
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react11.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.error, children: [
|
|
1723
|
+
"\u2717 ",
|
|
1724
|
+
w.name
|
|
1725
|
+
] }) }, `e-${w.name}`)
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
if (idleCount > 0) {
|
|
1729
|
+
slots.push(
|
|
1730
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react11.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.textDim, children: [
|
|
1731
|
+
"\u25CB ",
|
|
1732
|
+
idleCount,
|
|
1733
|
+
" idle"
|
|
1734
|
+
] }) }, "idle")
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { paddingX: 1, children: [
|
|
1738
|
+
anyWorking && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AnimationActiveSentinel, {}),
|
|
1739
|
+
slots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react11.default.Fragment, { children: [
|
|
1740
|
+
i > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.border, children: " \u2502 " }),
|
|
1741
|
+
slot
|
|
1742
|
+
] }, i)),
|
|
1743
|
+
pendingMessages > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1744
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.textDim, children: " " }),
|
|
1745
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.warning, children: [
|
|
1746
|
+
pendingMessages,
|
|
1747
|
+
" message",
|
|
1748
|
+
pendingMessages === 1 ? "" : "s",
|
|
1749
|
+
" queued"
|
|
1750
|
+
] })
|
|
1751
|
+
] })
|
|
1752
|
+
] });
|
|
1753
|
+
}
|
|
1754
|
+
function StaticRowView({ row }) {
|
|
1755
|
+
if (row.kind === "banner") {
|
|
1756
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BossBanner, { subtitle: "Orchestrator", showShortcuts: true }) });
|
|
1757
|
+
}
|
|
1758
|
+
if (row.kind === "user") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(UserMessage, { text: row.text });
|
|
1759
|
+
if (row.kind === "assistant") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AssistantRow, { item: row });
|
|
1760
|
+
if (row.kind === "tool") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ToolHistoryRow, { item: row });
|
|
1761
|
+
if (row.kind === "worker_event") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(WorkerEventRow, { item: row });
|
|
1762
|
+
if (row.kind === "worker_error") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(WorkerErrorRow, { item: row });
|
|
1763
|
+
if (row.kind === "info") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(InfoRow, { text: row.text, level: row.level ?? "info" });
|
|
1764
|
+
if (row.kind === "task_dispatch") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TaskDispatchRow, { tasks: row.tasks });
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
function TaskDispatchRow({
|
|
1768
|
+
tasks
|
|
1769
|
+
}) {
|
|
1770
|
+
const theme = useTheme();
|
|
1771
|
+
const count = tasks.length;
|
|
1772
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
1773
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
|
|
1774
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: COLORS.primary, bold: true, children: "\u23FA " }),
|
|
1775
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: theme.text, bold: true, children: [
|
|
1776
|
+
"Running ",
|
|
1777
|
+
count,
|
|
1778
|
+
" task",
|
|
1779
|
+
count === 1 ? "" : "s",
|
|
1780
|
+
":"
|
|
1781
|
+
] })
|
|
1782
|
+
] }),
|
|
1783
|
+
tasks.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
|
|
1784
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.textDim, children: " \u2022 " }),
|
|
1785
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: projectColor(t.project), bold: true, children: t.project }),
|
|
1786
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.textDim, children: ": " }),
|
|
1787
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.text, children: t.title })
|
|
1788
|
+
] }, `${t.project}-${i}`))
|
|
1789
|
+
] });
|
|
1790
|
+
}
|
|
1791
|
+
var SHORTCUT_PATTERNS = [
|
|
1792
|
+
// Modifier+Key combos: Ctrl+T, Shift+Tab, Cmd+K, Ctrl+Shift+P, Ctrl+C
|
|
1793
|
+
/\b(?:Ctrl|Cmd|Alt|Option|Opt|Shift|Meta|Win|Super)(?:\s*\+\s*(?:Ctrl|Cmd|Alt|Option|Opt|Shift|Meta|Win|Super))*\s*\+\s*(?:Tab|Enter|Esc|Escape|Space|Backspace|Delete|Del|Home|End|PageUp|PageDown|Up|Down|Left|Right|F[1-9]|F1[0-2]|[A-Z0-9]|\/|\?|\.|,|;|=|-)\b/g,
|
|
1794
|
+
// Bare named keys (only when surrounded by clear key context)
|
|
1795
|
+
/\b(?:Ctrl-[A-Z]|F[1-9]|F1[0-2])\b/g
|
|
1796
|
+
];
|
|
1797
|
+
function highlightShortcuts(text) {
|
|
1798
|
+
if (!text) return text;
|
|
1799
|
+
const SENTINEL = "\uE000";
|
|
1800
|
+
const masks = [];
|
|
1801
|
+
let masked = text.replace(/```[\s\S]*?```|`[^`]+`/g, (m) => {
|
|
1802
|
+
const idx = masks.push(m) - 1;
|
|
1803
|
+
return `${SENTINEL}${idx}${SENTINEL}`;
|
|
1804
|
+
});
|
|
1805
|
+
for (const re of SHORTCUT_PATTERNS) {
|
|
1806
|
+
masked = masked.replace(re, (m) => `\`${m}\``);
|
|
1807
|
+
}
|
|
1808
|
+
return masked.replace(
|
|
1809
|
+
new RegExp(`${SENTINEL}(\\d+)${SENTINEL}`, "g"),
|
|
1810
|
+
(_, i) => masks[Number(i)]
|
|
1811
|
+
);
|
|
1812
|
+
}
|
|
1813
|
+
function AssistantRow({ item }) {
|
|
1814
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1815
|
+
AssistantMessage,
|
|
1816
|
+
{
|
|
1817
|
+
text: highlightShortcuts(item.text),
|
|
1818
|
+
thinking: item.thinking,
|
|
1819
|
+
thinkingMs: item.thinkingMs
|
|
1820
|
+
}
|
|
1821
|
+
);
|
|
1822
|
+
}
|
|
1823
|
+
function ToolHistoryRow({ item }) {
|
|
1824
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1825
|
+
ToolExecution,
|
|
1826
|
+
{
|
|
1827
|
+
status: "done",
|
|
1828
|
+
name: item.name,
|
|
1829
|
+
args: item.args,
|
|
1830
|
+
result: item.result,
|
|
1831
|
+
isError: item.isError,
|
|
1832
|
+
details: item.details,
|
|
1833
|
+
formatters: bossToolFormatters
|
|
1834
|
+
}
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
function parseStatusGrade(text) {
|
|
1838
|
+
const matches = [...text.matchAll(/^\s*Status:\s*(DONE|UNVERIFIED|PARTIAL|BLOCKED|INFO)\b/gim)];
|
|
1839
|
+
const last = matches[matches.length - 1];
|
|
1840
|
+
if (!last) return null;
|
|
1841
|
+
return last[1].toUpperCase();
|
|
1842
|
+
}
|
|
1843
|
+
function parseWorkerTrailer(text) {
|
|
1844
|
+
const out = {};
|
|
1845
|
+
const grab = (label) => {
|
|
1846
|
+
const re = new RegExp(
|
|
1847
|
+
`^\\s*${label}:\\s*([\\s\\S]*?)(?=^\\s*(?:Changed|Skipped|Verified|Notes|Status):|$)`,
|
|
1848
|
+
"im"
|
|
1849
|
+
);
|
|
1850
|
+
const m = re.exec(text);
|
|
1851
|
+
if (!m) return void 0;
|
|
1852
|
+
const v = m[1].replace(/```[\s\S]*?```/g, "[code]").replace(/`([^`]+)`/g, "$1").replace(/\s+/g, " ").trim();
|
|
1853
|
+
return v.length > 0 ? v : void 0;
|
|
1854
|
+
};
|
|
1855
|
+
out.changed = grab("Changed");
|
|
1856
|
+
out.skipped = grab("Skipped");
|
|
1857
|
+
out.verified = grab("Verified");
|
|
1858
|
+
out.notes = grab("Notes");
|
|
1859
|
+
return out;
|
|
1860
|
+
}
|
|
1861
|
+
function clip(text, maxLen) {
|
|
1862
|
+
return text.length <= maxLen ? text : text.slice(0, Math.max(1, maxLen - 1)) + "\u2026";
|
|
1863
|
+
}
|
|
1864
|
+
function summarizeFinalText(text, maxLen) {
|
|
1865
|
+
if (!text) return "";
|
|
1866
|
+
const trailer = parseWorkerTrailer(text);
|
|
1867
|
+
const parts = [];
|
|
1868
|
+
if (trailer.changed) parts.push(`Changed: ${trailer.changed}`);
|
|
1869
|
+
if (trailer.verified) parts.push(`Verified: ${trailer.verified}`);
|
|
1870
|
+
if (trailer.skipped) parts.push(`Skipped: ${trailer.skipped}`);
|
|
1871
|
+
if (trailer.notes) parts.push(`Notes: ${trailer.notes}`);
|
|
1872
|
+
if (parts.length > 0) return clip(parts.join(" \xB7 "), maxLen);
|
|
1873
|
+
const beforeSummary = text.split(/^Changed:|^Skipped:|^Verified:|^Notes:|^Status:/im)[0];
|
|
1874
|
+
const stripped = beforeSummary.replace(/```[\s\S]*?```/g, "[code]").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/^\s*[-*]\s+/gm, "").replace(/^#+\s+/gm, "").replace(/\s+/g, " ").trim();
|
|
1875
|
+
if (!stripped) return "";
|
|
1876
|
+
const firstSentence = stripped.match(/^[^.!?\n]+[.!?]/);
|
|
1877
|
+
return clip(firstSentence ? firstSentence[0] : stripped, maxLen);
|
|
1878
|
+
}
|
|
1879
|
+
function statusGradeColor(grade, theme) {
|
|
1880
|
+
switch (grade) {
|
|
1881
|
+
case "DONE":
|
|
1882
|
+
return theme.success;
|
|
1883
|
+
case "UNVERIFIED":
|
|
1884
|
+
case "PARTIAL":
|
|
1885
|
+
return theme.warning;
|
|
1886
|
+
case "BLOCKED":
|
|
1887
|
+
return theme.error;
|
|
1888
|
+
case "INFO":
|
|
1889
|
+
return theme.textDim;
|
|
1890
|
+
default:
|
|
1891
|
+
return theme.textDim;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
function WorkerEventRow({ item }) {
|
|
1895
|
+
const theme = useTheme();
|
|
1896
|
+
const { columns } = useTerminalSize();
|
|
1897
|
+
const failedCount = item.toolsUsed.filter((t) => !t.ok).length;
|
|
1898
|
+
const total = item.toolsUsed.length;
|
|
1899
|
+
const grade = parseStatusGrade(item.finalText);
|
|
1900
|
+
const loaderStatus = grade === "BLOCKED" || failedCount > 0 ? "error" : grade === "UNVERIFIED" || grade === "PARTIAL" ? "queued" : "done";
|
|
1901
|
+
const headerColor = loaderStatus === "error" ? theme.toolError : projectColor(item.project);
|
|
1902
|
+
const toolSummary = total === 0 ? "no tools" : failedCount > 0 ? `${total} tools (${failedCount} failed)` : `${total} tool${total === 1 ? "" : "s"}`;
|
|
1903
|
+
const fieldMaxLen = Math.max(20, columns - 14);
|
|
1904
|
+
const trailer = parseWorkerTrailer(item.finalText);
|
|
1905
|
+
const hasTrailer = !!(trailer.changed || trailer.skipped || trailer.verified || trailer.notes);
|
|
1906
|
+
const fallbackSummary = hasTrailer ? "" : summarizeFinalText(item.finalText, fieldMaxLen);
|
|
1907
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
|
|
1908
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "row", children: [
|
|
1909
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ToolUseLoader, { status: loaderStatus }),
|
|
1910
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { wrap: "wrap", children: [
|
|
1911
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: headerColor, bold: true, children: item.project }),
|
|
1912
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.text, children: ` turn ${item.turnIndex}` }),
|
|
1913
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${toolSummary}` }),
|
|
1914
|
+
grade && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: statusGradeColor(grade, theme), bold: true, children: grade })
|
|
1917
|
+
] })
|
|
1918
|
+
] }) })
|
|
1919
|
+
] }),
|
|
1920
|
+
hasTrailer ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1921
|
+
trailer.changed && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TrailerLine, { label: "Changed", value: trailer.changed, maxLen: fieldMaxLen }),
|
|
1922
|
+
trailer.verified && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1923
|
+
TrailerLine,
|
|
1924
|
+
{
|
|
1925
|
+
label: "Verified",
|
|
1926
|
+
value: trailer.verified,
|
|
1927
|
+
maxLen: fieldMaxLen,
|
|
1928
|
+
labelColor: theme.success
|
|
1929
|
+
}
|
|
1930
|
+
),
|
|
1931
|
+
trailer.skipped && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1932
|
+
TrailerLine,
|
|
1933
|
+
{
|
|
1934
|
+
label: "Skipped",
|
|
1935
|
+
value: trailer.skipped,
|
|
1936
|
+
maxLen: fieldMaxLen,
|
|
1937
|
+
labelColor: theme.warning
|
|
1938
|
+
}
|
|
1939
|
+
),
|
|
1940
|
+
trailer.notes && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TrailerLine, { label: "Notes", value: trailer.notes, maxLen: fieldMaxLen })
|
|
1941
|
+
] }) : fallbackSummary && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.textDim, wrap: "truncate", children: fallbackSummary }) })
|
|
1942
|
+
] });
|
|
1943
|
+
}
|
|
1944
|
+
function TrailerLine({
|
|
1945
|
+
label,
|
|
1946
|
+
value,
|
|
1947
|
+
maxLen,
|
|
1948
|
+
labelColor
|
|
1949
|
+
}) {
|
|
1950
|
+
const theme = useTheme();
|
|
1951
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { wrap: "truncate", children: [
|
|
1952
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: labelColor ?? theme.textDim, bold: true, children: [
|
|
1953
|
+
label,
|
|
1954
|
+
":"
|
|
1955
|
+
] }),
|
|
1956
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.text, children: ` ${clip(value, maxLen - label.length - 2)}` })
|
|
1957
|
+
] }) });
|
|
1958
|
+
}
|
|
1959
|
+
function WorkerErrorRow({ item }) {
|
|
1960
|
+
const theme = useTheme();
|
|
1961
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
|
|
1962
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "row", children: [
|
|
1963
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ToolUseLoader, { status: "error" }),
|
|
1964
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { wrap: "wrap", children: [
|
|
1965
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.toolError, bold: true, children: item.project }),
|
|
1966
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.textDim, children: " worker error" })
|
|
1967
|
+
] }) })
|
|
1968
|
+
] }),
|
|
1969
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme.error, wrap: "wrap", children: item.message }) })
|
|
1970
|
+
] });
|
|
1971
|
+
}
|
|
1972
|
+
function InfoRow({
|
|
1973
|
+
text,
|
|
1974
|
+
level
|
|
1975
|
+
}) {
|
|
1976
|
+
if (level === "info") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AssistantMessage, { text });
|
|
1977
|
+
const theme = useTheme();
|
|
1978
|
+
const color = level === "error" ? theme.error : theme.warning;
|
|
1979
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { marginTop: 1, flexDirection: "row", children: [
|
|
1980
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ToolUseLoader, { status: level === "error" ? "error" : "queued" }),
|
|
1981
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color, wrap: "wrap", children: text }) })
|
|
1982
|
+
] });
|
|
1983
|
+
}
|
|
1984
|
+
function StreamingTurnView({
|
|
1985
|
+
turn,
|
|
1986
|
+
isRunning
|
|
1987
|
+
}) {
|
|
1988
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
1989
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1990
|
+
StreamingArea,
|
|
1991
|
+
{
|
|
1992
|
+
isRunning,
|
|
1993
|
+
streamingText: turn.text,
|
|
1994
|
+
streamingThinking: turn.thinking,
|
|
1995
|
+
thinkingMs: turn.thinkingMs
|
|
1996
|
+
}
|
|
1997
|
+
),
|
|
1998
|
+
turn.tools.map((t) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StreamingToolRow, { tool: t }, t.toolCallId))
|
|
1999
|
+
] });
|
|
2000
|
+
}
|
|
2001
|
+
function StreamingToolRow({ tool }) {
|
|
2002
|
+
if (tool.status === "running") {
|
|
2003
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2004
|
+
ToolExecution,
|
|
2005
|
+
{
|
|
2006
|
+
status: "running",
|
|
2007
|
+
name: tool.name,
|
|
2008
|
+
args: tool.args,
|
|
2009
|
+
formatters: bossToolFormatters
|
|
2010
|
+
}
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
2013
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2014
|
+
ToolExecution,
|
|
2015
|
+
{
|
|
2016
|
+
status: "done",
|
|
2017
|
+
name: tool.name,
|
|
2018
|
+
args: tool.args,
|
|
2019
|
+
result: tool.result ?? "",
|
|
2020
|
+
isError: tool.status === "error",
|
|
2021
|
+
details: tool.details,
|
|
2022
|
+
formatters: bossToolFormatters
|
|
2023
|
+
}
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
function renderBossApp(opts) {
|
|
2027
|
+
const instance = render_default(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BossApp, { boss: opts.boss }), { exitOnCtrlC: false });
|
|
2028
|
+
return {
|
|
2029
|
+
waitUntilExit: async () => {
|
|
2030
|
+
await instance.waitUntilExit();
|
|
2031
|
+
},
|
|
2032
|
+
unmount: () => instance.unmount()
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// src/splash.tsx
|
|
2037
|
+
init_esm_shims();
|
|
2038
|
+
var import_react12 = __toESM(require_react(), 1);
|
|
2039
|
+
var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
|
|
2040
|
+
var SPLASH_LINES = [
|
|
2041
|
+
" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ",
|
|
2042
|
+
" \u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2588\u2588\u2588 \u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2588\u2588\u2588 \u2591\u2591\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2588\u2588\u2588 ",
|
|
2043
|
+
" \u2588\u2588\u2588 \u2591\u2591\u2591 \u2588\u2588\u2588 \u2591\u2591\u2591 \u2591\u2588\u2588\u2588 \u2591\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 ",
|
|
2044
|
+
"\u2591\u2588\u2588\u2588 \u2591\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2591\u2591\u2588\u2588\u2588 \u2588\u2588\u2588\u2591\u2591 \u2588\u2588\u2588\u2591\u2591 ",
|
|
2045
|
+
"\u2591\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2591\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2588\u2588\u2588\u2591\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2591\u2591\u2588\u2588\u2588\u2588\u2588 \u2591\u2591\u2588\u2588\u2588\u2588\u2588 ",
|
|
2046
|
+
"\u2591\u2591\u2588\u2588\u2588 \u2591\u2591\u2588\u2588\u2588 \u2591\u2591\u2588\u2588\u2588 \u2591\u2591\u2588\u2588\u2588 \u2591\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2591\u2588\u2588\u2588 \u2591\u2588\u2588\u2588 \u2591\u2591\u2591\u2591\u2588\u2588\u2588 \u2591\u2591\u2591\u2591\u2588\u2588\u2588",
|
|
2047
|
+
" \u2591\u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2591\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 ",
|
|
2048
|
+
" \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 \u2591\u2591\u2591\u2591\u2591\u2591 \u2591\u2591\u2591\u2591\u2591\u2591 \u2591\u2591\u2591\u2591\u2591\u2591 "
|
|
2049
|
+
];
|
|
2050
|
+
var SPLASH_WIDTH = SPLASH_LINES[0].length;
|
|
2051
|
+
function colorForLine(lineIdx, totalLines, offset) {
|
|
2052
|
+
const t = totalLines <= 1 ? 0 : (lineIdx + offset) % totalLines;
|
|
2053
|
+
const idx = Math.floor(t / totalLines * GRADIENT.length) % GRADIENT.length;
|
|
2054
|
+
return GRADIENT[idx];
|
|
2055
|
+
}
|
|
2056
|
+
function SplashLogo({ offset }) {
|
|
2057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { flexDirection: "column", children: SPLASH_LINES.map((line, i) => {
|
|
2058
|
+
const hue = colorForLine(i, SPLASH_LINES.length, offset);
|
|
2059
|
+
const segments = [];
|
|
2060
|
+
let buf = "";
|
|
2061
|
+
let bufDim = false;
|
|
2062
|
+
for (const ch of line) {
|
|
2063
|
+
const dim = ch === "\u2591";
|
|
2064
|
+
if (segments.length === 0 && buf.length === 0) {
|
|
2065
|
+
buf = ch;
|
|
2066
|
+
bufDim = dim;
|
|
2067
|
+
continue;
|
|
2068
|
+
}
|
|
2069
|
+
if (dim === bufDim) {
|
|
2070
|
+
buf += ch;
|
|
2071
|
+
} else {
|
|
2072
|
+
segments.push({ text: buf, dim: bufDim });
|
|
2073
|
+
buf = ch;
|
|
2074
|
+
bufDim = dim;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
if (buf) segments.push({ text: buf, dim: bufDim });
|
|
2078
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: segments.map((seg, j) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: hue, dimColor: seg.dim, children: seg.text }, j)) }, i);
|
|
2079
|
+
}) });
|
|
2080
|
+
}
|
|
2081
|
+
function SplashScreen({ caption }) {
|
|
2082
|
+
const [offset, setOffset] = (0, import_react12.useState)(0);
|
|
2083
|
+
(0, import_react12.useEffect)(() => {
|
|
2084
|
+
const timer = setInterval(() => {
|
|
2085
|
+
setOffset((o) => o + 1);
|
|
2086
|
+
}, 120);
|
|
2087
|
+
return () => {
|
|
2088
|
+
clearInterval(timer);
|
|
2089
|
+
};
|
|
2090
|
+
}, []);
|
|
2091
|
+
const [size, setSize] = (0, import_react12.useState)(() => ({
|
|
2092
|
+
columns: process.stdout.columns ?? 80,
|
|
2093
|
+
rows: process.stdout.rows ?? 24
|
|
2094
|
+
}));
|
|
2095
|
+
(0, import_react12.useEffect)(() => {
|
|
2096
|
+
const handler = () => setSize({
|
|
2097
|
+
columns: process.stdout.columns ?? 80,
|
|
2098
|
+
rows: process.stdout.rows ?? 24
|
|
164
2099
|
});
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
2100
|
+
process.stdout.on("resize", handler);
|
|
2101
|
+
return () => {
|
|
2102
|
+
process.stdout.off("resize", handler);
|
|
2103
|
+
};
|
|
2104
|
+
}, []);
|
|
2105
|
+
const SPLASH_BLOCK_HEIGHT = SPLASH_LINES.length + 3;
|
|
2106
|
+
const verticalPad = Math.max(0, Math.floor((size.rows - SPLASH_BLOCK_HEIGHT) / 2));
|
|
2107
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", width: size.columns, height: size.rows, alignItems: "center", children: [
|
|
2108
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { height: verticalPad, flexShrink: 0 }),
|
|
2109
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", alignItems: "flex-start", flexShrink: 0, children: [
|
|
2110
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SplashLogo, { offset }),
|
|
2111
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { width: SPLASH_WIDTH, marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { children: [
|
|
2112
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: COLORS.text, bold: true, children: BRAND }),
|
|
2113
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { color: COLORS.textDim, children: [
|
|
2114
|
+
" v",
|
|
2115
|
+
VERSION
|
|
2116
|
+
] }),
|
|
2117
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: COLORS.textDim, children: " \xB7 By " }),
|
|
2118
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: COLORS.text, bold: true, children: AUTHOR })
|
|
2119
|
+
] }) }),
|
|
2120
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { width: SPLASH_WIDTH, justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: COLORS.textDim, children: caption ?? "Spinning up the orchestrator\u2026" }) })
|
|
2121
|
+
] })
|
|
2122
|
+
] });
|
|
2123
|
+
}
|
|
2124
|
+
function showSplash(opts) {
|
|
2125
|
+
const start = Date.now();
|
|
2126
|
+
void playSplashAudio();
|
|
2127
|
+
const instance = render_default(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SplashScreen, { caption: opts.caption }));
|
|
2128
|
+
const audioDurationMs = getSplashAudioDurationMs();
|
|
2129
|
+
const defaultMinMs = audioDurationMs + 200;
|
|
2130
|
+
return {
|
|
2131
|
+
dismiss: async () => {
|
|
2132
|
+
const minMs = opts.minMs ?? defaultMinMs;
|
|
2133
|
+
const elapsed = Date.now() - start;
|
|
2134
|
+
const remaining = Math.max(0, minMs - elapsed);
|
|
2135
|
+
if (remaining > 0) {
|
|
2136
|
+
await new Promise((r) => setTimeout(r, remaining));
|
|
2137
|
+
}
|
|
2138
|
+
instance.unmount();
|
|
2139
|
+
await new Promise((r) => setImmediate(r));
|
|
2140
|
+
}
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// src/cli.ts
|
|
2145
|
+
function parseProjectSpec(raw) {
|
|
2146
|
+
const eq = raw.indexOf("=");
|
|
2147
|
+
if (eq > 0) {
|
|
2148
|
+
const name = raw.slice(0, eq);
|
|
2149
|
+
const cwd2 = path4.resolve(raw.slice(eq + 1));
|
|
2150
|
+
return { name, cwd: cwd2 };
|
|
2151
|
+
}
|
|
2152
|
+
const cwd = path4.resolve(raw);
|
|
2153
|
+
return { name: path4.basename(cwd), cwd };
|
|
2154
|
+
}
|
|
2155
|
+
function parseArgs(argv) {
|
|
2156
|
+
const args = {
|
|
2157
|
+
projects: []
|
|
2158
|
+
};
|
|
2159
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2160
|
+
const a = argv[i];
|
|
2161
|
+
if (a === "--project" || a === "-p") {
|
|
2162
|
+
const v = argv[++i];
|
|
2163
|
+
if (!v) throw new Error("--project requires a value");
|
|
2164
|
+
args.projects.push(parseProjectSpec(v));
|
|
2165
|
+
} else if (a === "--boss-model") {
|
|
2166
|
+
const v = argv[++i];
|
|
2167
|
+
if (!v) throw new Error("--boss-model requires a value");
|
|
2168
|
+
args.bossModel = v;
|
|
2169
|
+
} else if (a === "--worker-model") {
|
|
2170
|
+
const v = argv[++i];
|
|
2171
|
+
if (!v) throw new Error("--worker-model requires a value");
|
|
2172
|
+
args.workerModel = v;
|
|
2173
|
+
} else if (a === "--resume") {
|
|
2174
|
+
const v = argv[++i];
|
|
2175
|
+
if (!v) throw new Error("--resume requires a session id");
|
|
2176
|
+
args.resumeSessionId = v;
|
|
2177
|
+
} else if (a === "--help" || a === "-h") {
|
|
2178
|
+
printHelpAndExit();
|
|
2179
|
+
} else {
|
|
2180
|
+
throw new Error(`Unknown argument: ${a}`);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
return args;
|
|
2184
|
+
}
|
|
2185
|
+
function printHelpAndExit() {
|
|
2186
|
+
const c = (color, text) => source_default.hex(color)(text);
|
|
2187
|
+
process.stdout.write(
|
|
2188
|
+
"\n" + c(COLORS.primary, "GG Boss") + c(COLORS.textDim, " \u2014 orchestrator that drives multiple ggcoder workers from one chat.\n\n") + c(COLORS.text, "Usage\n") + " " + c(COLORS.accent, "ggboss") + c(
|
|
2189
|
+
COLORS.textDim,
|
|
2190
|
+
" start orchestrator using linked projects\n"
|
|
2191
|
+
) + " " + c(COLORS.accent, "ggboss link") + c(COLORS.textDim, " pick which projects to link (interactive)\n") + " " + c(COLORS.accent, "ggboss continue") + c(COLORS.textDim, " resume the most recent boss session\n") + " " + c(COLORS.accent, "ggboss --resume <id>") + c(COLORS.textDim, " resume a specific boss session\n") + " " + c(COLORS.accent, "ggboss --project <spec> [...]") + c(COLORS.textDim, " override links with explicit project(s)\n\n") + c(COLORS.text, "Options\n") + " " + c(COLORS.primary, "--project, -p <spec>") + c(COLORS.textDim, ' project to manage. spec is "cwd" or "name=cwd". repeatable.\n') + " " + c(COLORS.primary, "--boss-model <id>") + c(COLORS.textDim, " model for the orchestrator (default: claude-opus-4-7)\n") + " " + c(COLORS.primary, "--worker-model <id>") + c(COLORS.textDim, " model for workers (default: claude-sonnet-4-6)\n") + " " + c(COLORS.primary, "--help, -h") + c(COLORS.textDim, " show this help\n\n") + c(COLORS.textDim, "Talk to the boss at the prompt. Press ") + c(COLORS.accent, "Ctrl+C") + c(COLORS.textDim, " twice to exit.\n\n")
|
|
2192
|
+
);
|
|
2193
|
+
process.exit(0);
|
|
2194
|
+
}
|
|
2195
|
+
async function runOrchestrator(args) {
|
|
2196
|
+
if (args.projects.length === 0) {
|
|
2197
|
+
const links = await loadLinks();
|
|
2198
|
+
if (links.projects.length === 0) {
|
|
2199
|
+
process.stderr.write(
|
|
2200
|
+
"\n" + source_default.hex(COLORS.warning)("No linked projects.") + source_default.hex(COLORS.textDim)(" Run ") + source_default.hex(COLORS.accent)("ggboss link") + source_default.hex(COLORS.textDim)(" to choose, or pass ") + source_default.hex(COLORS.accent)("--project") + source_default.hex(COLORS.textDim)(".\n\n")
|
|
2201
|
+
);
|
|
2202
|
+
process.exit(1);
|
|
2203
|
+
}
|
|
2204
|
+
args.projects = links.projects.map((p) => ({ name: p.name, cwd: p.cwd }));
|
|
2205
|
+
}
|
|
2206
|
+
clearScreen();
|
|
2207
|
+
const splash = showSplash({
|
|
2208
|
+
caption: `Spinning up ${args.projects.length} worker${args.projects.length === 1 ? "" : "s"}\u2026`
|
|
2209
|
+
});
|
|
2210
|
+
const settings = await loadSettings();
|
|
2211
|
+
const finalBossProvider = args.bossProvider ?? settings.bossProvider ?? "anthropic";
|
|
2212
|
+
const finalBossModel = args.bossModel ?? settings.bossModel ?? "claude-opus-4-7";
|
|
2213
|
+
const finalWorkerProvider = args.workerProvider ?? settings.workerProvider ?? "anthropic";
|
|
2214
|
+
const finalWorkerModel = args.workerModel ?? settings.workerModel ?? "claude-sonnet-4-6";
|
|
2215
|
+
initLogger({
|
|
2216
|
+
version: VERSION,
|
|
2217
|
+
bossProvider: finalBossProvider,
|
|
2218
|
+
bossModel: finalBossModel,
|
|
2219
|
+
bossThinking: settings.bossThinkingLevel,
|
|
2220
|
+
workerProvider: finalWorkerProvider,
|
|
2221
|
+
workerModel: finalWorkerModel,
|
|
2222
|
+
projectCount: args.projects.length
|
|
2223
|
+
});
|
|
2224
|
+
log("INFO", "cli", "linked projects", {
|
|
2225
|
+
projects: args.projects.map((p) => p.name).join(",")
|
|
2226
|
+
});
|
|
2227
|
+
const updateMessage = checkAndAutoUpdate(VERSION);
|
|
2228
|
+
if (updateMessage) log("INFO", "auto_update", updateMessage);
|
|
2229
|
+
const boss = new GGBoss({
|
|
2230
|
+
bossProvider: finalBossProvider,
|
|
2231
|
+
bossModel: finalBossModel,
|
|
2232
|
+
bossThinkingLevel: settings.bossThinkingLevel,
|
|
2233
|
+
workerProvider: finalWorkerProvider,
|
|
2234
|
+
workerModel: finalWorkerModel,
|
|
2235
|
+
projects: args.projects,
|
|
2236
|
+
continueRecent: args.continueRecent,
|
|
2237
|
+
resumeSessionId: args.resumeSessionId
|
|
2238
|
+
});
|
|
2239
|
+
await boss.initialize();
|
|
2240
|
+
await splash.dismiss();
|
|
2241
|
+
clearScreen();
|
|
2242
|
+
const ink = renderBossApp({ boss });
|
|
2243
|
+
const runPromise = boss.run();
|
|
2244
|
+
await ink.waitUntilExit();
|
|
2245
|
+
await boss.dispose();
|
|
2246
|
+
stopRadio();
|
|
2247
|
+
await runPromise.catch(() => {
|
|
2248
|
+
});
|
|
2249
|
+
process.exit(0);
|
|
183
2250
|
}
|
|
184
2251
|
async function main() {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (isContinue)
|
|
195
|
-
args.continueRecent = true;
|
|
196
|
-
await runOrchestrator(args);
|
|
2252
|
+
const argv = process.argv.slice(2);
|
|
2253
|
+
if (argv[0] === "link") {
|
|
2254
|
+
await runLinkCommand();
|
|
2255
|
+
process.exit(0);
|
|
2256
|
+
}
|
|
2257
|
+
const isContinue = argv[0] === "continue";
|
|
2258
|
+
const args = parseArgs(isContinue ? argv.slice(1) : argv);
|
|
2259
|
+
if (isContinue) args.continueRecent = true;
|
|
2260
|
+
await runOrchestrator(args);
|
|
197
2261
|
}
|
|
198
2262
|
main().catch((err) => {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
2263
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2264
|
+
process.stderr.write(source_default.hex(COLORS.error)(`
|
|
2265
|
+
gg-boss failed: ${message}
|
|
2266
|
+
`));
|
|
2267
|
+
process.exit(1);
|
|
202
2268
|
});
|
|
203
2269
|
//# sourceMappingURL=cli.js.map
|