@kenkaiiii/gg-boss 4.3.163 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/{chunk-JBKZOBJ7.js → chunk-5ENJR6XI.js} +33489 -19470
- package/dist/{chunk-JBKZOBJ7.js.map → chunk-5ENJR6XI.js.map} +1 -1
- package/dist/{chunk-YNWFCUMR.js → chunk-DZO3FVYX.js} +3 -3
- package/dist/chunk-DZO3FVYX.js.map +1 -0
- package/dist/{chunk-QT366Y52.js → chunk-JEGMYLRS.js} +3 -3
- package/dist/{chunk-WJ4S4TOY.js → chunk-NA54UQR4.js} +2 -2
- package/dist/cli.js +1679 -1378
- package/dist/cli.js.map +1 -1
- package/dist/{devtools-526EIB4G.js → devtools-XF5S3NSL.js} +11 -33
- package/dist/{devtools-526EIB4G.js.map → devtools-XF5S3NSL.js.map} +1 -1
- package/dist/{dist-VXOVSHZ5.js → dist-IGN2W3JX.js} +2 -2
- package/dist/{chunk-EZYGVECW.js → ignore-3XU7YNRW.js} +3 -6
- package/dist/index.js +4 -6
- package/dist/index.js.map +1 -1
- package/dist/{chunk-RMSZMSH5.js → out-NHVJUVVH.js} +3 -6
- package/dist/{pixel-WPYTQADG.js → pixel-OQO4WMWJ.js} +4 -4
- package/dist/{pixel-fix-4WGZAJ5W.js → pixel-fix-JKVDORFT.js} +3 -3
- package/package.json +6 -5
- package/dist/chunk-YNWFCUMR.js.map +0 -1
- package/dist/ignore-AXNNXJD4.js +0 -7
- package/dist/out-NH6HQBFM.js +0 -7
- package/dist/out-NH6HQBFM.js.map +0 -1
- package/dist/pixel-WPYTQADG.js.map +0 -1
- /package/dist/{chunk-QT366Y52.js.map → chunk-JEGMYLRS.js.map} +0 -0
- /package/dist/{chunk-WJ4S4TOY.js.map → chunk-NA54UQR4.js.map} +0 -0
- /package/dist/{dist-VXOVSHZ5.js.map → dist-IGN2W3JX.js.map} +0 -0
- /package/dist/{chunk-EZYGVECW.js.map → ignore-3XU7YNRW.js.map} +0 -0
- /package/dist/{chunk-RMSZMSH5.js.map → out-NHVJUVVH.js.map} +0 -0
- /package/dist/{ignore-AXNNXJD4.js.map → pixel-OQO4WMWJ.js.map} +0 -0
- /package/dist/{pixel-fix-4WGZAJ5W.js.map → pixel-fix-JKVDORFT.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,33 +1,50 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --max-old-space-size=8192 --expose-gc
|
|
2
2
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
|
-
ActivityIndicator,
|
|
5
4
|
AnimationProvider,
|
|
6
5
|
AssistantMessage,
|
|
6
|
+
AuthStorage,
|
|
7
7
|
Box_default,
|
|
8
|
+
ChatControls,
|
|
9
|
+
ChatInputStack,
|
|
10
|
+
ChatLayout,
|
|
11
|
+
ChatLivePane,
|
|
8
12
|
CompactionDone,
|
|
9
13
|
CompactionSpinner,
|
|
10
14
|
GGBoss,
|
|
11
15
|
InputArea,
|
|
12
16
|
MODELS,
|
|
13
17
|
MessageResponse,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Static,
|
|
17
|
-
StreamingArea,
|
|
18
|
+
RESPONSE_LEFT_PADDING,
|
|
19
|
+
TelegramBot,
|
|
18
20
|
TerminalSizeProvider,
|
|
19
21
|
Text,
|
|
20
22
|
ThemeContext,
|
|
21
23
|
ToolExecution,
|
|
24
|
+
ToolGroupExecution,
|
|
22
25
|
ToolUseLoader,
|
|
26
|
+
TranscriptItemFrame,
|
|
23
27
|
UserMessage,
|
|
24
28
|
bossStore,
|
|
29
|
+
bossToolGroupRenderers,
|
|
30
|
+
buildToolGroupSummary,
|
|
25
31
|
closeLogger,
|
|
32
|
+
color,
|
|
33
|
+
createAutoUpdater,
|
|
34
|
+
dim,
|
|
35
|
+
formatHistoryWrite,
|
|
26
36
|
getAppPaths,
|
|
27
37
|
getBossState,
|
|
28
38
|
getContextWindow,
|
|
39
|
+
getDefaultModel,
|
|
40
|
+
getModel,
|
|
41
|
+
getNextThinkingLevel,
|
|
29
42
|
getSplashAudioDurationMs,
|
|
43
|
+
getTranscriptItemMarginTop,
|
|
44
|
+
gradientLine,
|
|
45
|
+
indent,
|
|
30
46
|
initLogger,
|
|
47
|
+
isModelLoaded,
|
|
31
48
|
loadSettings,
|
|
32
49
|
loadTheme,
|
|
33
50
|
log,
|
|
@@ -36,9 +53,18 @@ import {
|
|
|
36
53
|
require_jsx_runtime,
|
|
37
54
|
require_react,
|
|
38
55
|
saveSettings,
|
|
56
|
+
serializeCompletedItemToTerminalHistory,
|
|
57
|
+
setProgressCallback,
|
|
39
58
|
setStreamDiagnostic,
|
|
59
|
+
shouldSeparateTranscriptItemKinds,
|
|
60
|
+
shouldTopSpaceStreamingAssistant,
|
|
61
|
+
stripAnsi,
|
|
62
|
+
stripTerminalFocusSequences,
|
|
40
63
|
subscribeToBossStore,
|
|
41
64
|
tasksStore,
|
|
65
|
+
toolTonePalette,
|
|
66
|
+
transcribeVoice,
|
|
67
|
+
truncatePlain,
|
|
42
68
|
useAnimationActive,
|
|
43
69
|
useAnimationTick,
|
|
44
70
|
useBossState,
|
|
@@ -48,18 +74,17 @@ import {
|
|
|
48
74
|
useTheme,
|
|
49
75
|
use_app_default,
|
|
50
76
|
use_input_default,
|
|
51
|
-
use_stdout_default
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import "./chunk-
|
|
55
|
-
import "./chunk-QT366Y52.js";
|
|
77
|
+
use_stdout_default,
|
|
78
|
+
wrapPlain
|
|
79
|
+
} from "./chunk-5ENJR6XI.js";
|
|
80
|
+
import "./chunk-JEGMYLRS.js";
|
|
56
81
|
import {
|
|
57
82
|
source_default
|
|
58
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-NA54UQR4.js";
|
|
59
84
|
import {
|
|
60
85
|
__toESM,
|
|
61
86
|
init_esm_shims
|
|
62
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-DZO3FVYX.js";
|
|
63
88
|
|
|
64
89
|
// src/cli.ts
|
|
65
90
|
init_esm_shims();
|
|
@@ -344,7 +369,7 @@ init_esm_shims();
|
|
|
344
369
|
// package.json
|
|
345
370
|
var package_default = {
|
|
346
371
|
name: "@kenkaiiii/gg-boss",
|
|
347
|
-
version: "4.
|
|
372
|
+
version: "4.4.0",
|
|
348
373
|
type: "module",
|
|
349
374
|
description: "Orchestrator agent that drives multiple ggcoder sessions across projects from a single chat",
|
|
350
375
|
license: "MIT",
|
|
@@ -370,11 +395,12 @@ var package_default = {
|
|
|
370
395
|
devDependencies: {
|
|
371
396
|
"@kenkaiiii/gg-agent": "workspace:*",
|
|
372
397
|
"@kenkaiiii/gg-ai": "workspace:*",
|
|
398
|
+
"@kenkaiiii/gg-core": "workspace:*",
|
|
373
399
|
"@kenkaiiii/ggcoder": "workspace:*",
|
|
374
400
|
"@types/node": "^25.6.0",
|
|
375
401
|
"@types/react": "^19.2.14",
|
|
376
402
|
chalk: "^5.6.2",
|
|
377
|
-
ink: "
|
|
403
|
+
ink: "6.8.0",
|
|
378
404
|
react: "^19.2.5",
|
|
379
405
|
tsup: "^8.5.1",
|
|
380
406
|
typescript: "^6.0.3",
|
|
@@ -422,24 +448,6 @@ var GRADIENT = [
|
|
|
422
448
|
"#b91c1c"
|
|
423
449
|
// red-700 (slight darker tail)
|
|
424
450
|
];
|
|
425
|
-
var PULSE_COLORS = [
|
|
426
|
-
"#dc2626",
|
|
427
|
-
// crimson
|
|
428
|
-
"#e11d48",
|
|
429
|
-
// rose
|
|
430
|
-
"#be185d",
|
|
431
|
-
// wine
|
|
432
|
-
"#a21caf",
|
|
433
|
-
// magenta
|
|
434
|
-
"#c026d3",
|
|
435
|
-
// fuchsia
|
|
436
|
-
"#a21caf",
|
|
437
|
-
// back
|
|
438
|
-
"#be185d",
|
|
439
|
-
// back
|
|
440
|
-
"#e11d48"
|
|
441
|
-
// back
|
|
442
|
-
];
|
|
443
451
|
var COLORS = {
|
|
444
452
|
primary: "#e11d48",
|
|
445
453
|
// crimson-rose — main brand color
|
|
@@ -502,9 +510,9 @@ function GradientText({ text }) {
|
|
|
502
510
|
if (ch === " ") {
|
|
503
511
|
chars.push(ch);
|
|
504
512
|
} else {
|
|
505
|
-
const
|
|
513
|
+
const color2 = GRADIENT[colorIdx % GRADIENT.length];
|
|
506
514
|
chars.push(
|
|
507
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color, children: ch }, i)
|
|
515
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: color2, children: ch }, i)
|
|
508
516
|
);
|
|
509
517
|
colorIdx++;
|
|
510
518
|
}
|
|
@@ -670,317 +678,6 @@ async function runLinkCommand() {
|
|
|
670
678
|
init_esm_shims();
|
|
671
679
|
import path3 from "path";
|
|
672
680
|
import fs3 from "fs/promises";
|
|
673
|
-
|
|
674
|
-
// src/voice-transcriber.ts
|
|
675
|
-
init_esm_shims();
|
|
676
|
-
var TARGET_SAMPLE_RATE = 16e3;
|
|
677
|
-
var MODEL_ID = "Xenova/whisper-tiny.en";
|
|
678
|
-
var transcriber = null;
|
|
679
|
-
var loadPromise = null;
|
|
680
|
-
var onProgress = null;
|
|
681
|
-
function setProgressCallback(cb) {
|
|
682
|
-
onProgress = cb;
|
|
683
|
-
}
|
|
684
|
-
function resample(audio, fromRate, toRate) {
|
|
685
|
-
if (fromRate === toRate) return audio;
|
|
686
|
-
const ratio = fromRate / toRate;
|
|
687
|
-
const newLength = Math.round(audio.length / ratio);
|
|
688
|
-
const result = new Float32Array(newLength);
|
|
689
|
-
for (let i = 0; i < newLength; i++) {
|
|
690
|
-
const srcIndex = i * ratio;
|
|
691
|
-
const low = Math.floor(srcIndex);
|
|
692
|
-
const high = Math.min(low + 1, audio.length - 1);
|
|
693
|
-
const frac = srcIndex - low;
|
|
694
|
-
result[i] = audio[low] * (1 - frac) + audio[high] * frac;
|
|
695
|
-
}
|
|
696
|
-
return result;
|
|
697
|
-
}
|
|
698
|
-
function downmixToMono(channelData) {
|
|
699
|
-
if (channelData.length === 0) return new Float32Array();
|
|
700
|
-
if (channelData.length === 1) return channelData[0];
|
|
701
|
-
const samples = channelData[0].length;
|
|
702
|
-
const out = new Float32Array(samples);
|
|
703
|
-
const scale = 1 / channelData.length;
|
|
704
|
-
for (let i = 0; i < samples; i++) {
|
|
705
|
-
let mixed = 0;
|
|
706
|
-
for (const channel of channelData) mixed += channel[i] ?? 0;
|
|
707
|
-
out[i] = mixed * scale;
|
|
708
|
-
}
|
|
709
|
-
return out;
|
|
710
|
-
}
|
|
711
|
-
async function decodeOggOpus(buffer) {
|
|
712
|
-
const { OggOpusDecoder } = await import("ogg-opus-decoder");
|
|
713
|
-
const decoder = new OggOpusDecoder();
|
|
714
|
-
await decoder.ready;
|
|
715
|
-
try {
|
|
716
|
-
const decoded = await decoder.decodeFile(buffer);
|
|
717
|
-
if (!decoded.channelData?.length || !decoded.channelData[0]?.length) {
|
|
718
|
-
throw new Error("Decoded audio is empty");
|
|
719
|
-
}
|
|
720
|
-
const mono = downmixToMono(decoded.channelData);
|
|
721
|
-
return resample(mono, decoded.sampleRate, TARGET_SAMPLE_RATE);
|
|
722
|
-
} finally {
|
|
723
|
-
decoder.free();
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
async function getTranscriber() {
|
|
727
|
-
if (transcriber) return transcriber;
|
|
728
|
-
if (!loadPromise) {
|
|
729
|
-
loadPromise = (async () => {
|
|
730
|
-
const { pipeline } = await import("@huggingface/transformers");
|
|
731
|
-
const instance = await pipeline("automatic-speech-recognition", MODEL_ID, {
|
|
732
|
-
dtype: "fp32",
|
|
733
|
-
progress_callback: onProgress ?? void 0
|
|
734
|
-
});
|
|
735
|
-
transcriber = instance;
|
|
736
|
-
return instance;
|
|
737
|
-
})();
|
|
738
|
-
}
|
|
739
|
-
return loadPromise;
|
|
740
|
-
}
|
|
741
|
-
function isModelLoaded() {
|
|
742
|
-
return transcriber !== null;
|
|
743
|
-
}
|
|
744
|
-
async function transcribeVoice(fileUrl) {
|
|
745
|
-
const response = await fetch(fileUrl);
|
|
746
|
-
if (!response.ok) throw new Error(`Failed to download voice file: ${response.status}`);
|
|
747
|
-
const buffer = new Uint8Array(await response.arrayBuffer());
|
|
748
|
-
const pcm = await decodeOggOpus(buffer);
|
|
749
|
-
const asr = await getTranscriber();
|
|
750
|
-
const result = await asr(pcm);
|
|
751
|
-
const text = Array.isArray(result) ? result[0]?.text : result.text;
|
|
752
|
-
return (text ?? "").trim();
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
// src/telegram.ts
|
|
756
|
-
init_esm_shims();
|
|
757
|
-
var TELEGRAM_API = "https://api.telegram.org";
|
|
758
|
-
var MAX_MESSAGE_LENGTH = 4096;
|
|
759
|
-
var TelegramBot = class {
|
|
760
|
-
token;
|
|
761
|
-
allowedUserId;
|
|
762
|
-
offset = 0;
|
|
763
|
-
running = false;
|
|
764
|
-
onMessage = null;
|
|
765
|
-
onVoiceMessage = null;
|
|
766
|
-
onCallback = null;
|
|
767
|
-
onBotAdded = null;
|
|
768
|
-
onBotRemoved = null;
|
|
769
|
-
constructor(config) {
|
|
770
|
-
this.token = config.botToken;
|
|
771
|
-
this.allowedUserId = config.allowedUserId;
|
|
772
|
-
}
|
|
773
|
-
/** Register handler for incoming text messages. */
|
|
774
|
-
onText(handler) {
|
|
775
|
-
this.onMessage = handler;
|
|
776
|
-
}
|
|
777
|
-
/** Register handler for incoming voice notes. */
|
|
778
|
-
onVoice(handler) {
|
|
779
|
-
this.onVoiceMessage = handler;
|
|
780
|
-
}
|
|
781
|
-
/** Register handler for inline keyboard button presses. */
|
|
782
|
-
onCallbackQuery(handler) {
|
|
783
|
-
this.onCallback = handler;
|
|
784
|
-
}
|
|
785
|
-
/** Register handler for when the bot is added to a group. */
|
|
786
|
-
onAddedToGroup(handler) {
|
|
787
|
-
this.onBotAdded = handler;
|
|
788
|
-
}
|
|
789
|
-
/** Register handler for when the bot is removed from a group. */
|
|
790
|
-
onRemovedFromGroup(handler) {
|
|
791
|
-
this.onBotRemoved = handler;
|
|
792
|
-
}
|
|
793
|
-
/** Start long polling. Blocks until stop() is called. */
|
|
794
|
-
async start() {
|
|
795
|
-
this.running = true;
|
|
796
|
-
const me = await this.apiCall("getMe");
|
|
797
|
-
if (!me.ok) {
|
|
798
|
-
throw new Error(`Invalid bot token: ${JSON.stringify(me)}`);
|
|
799
|
-
}
|
|
800
|
-
while (this.running) {
|
|
801
|
-
try {
|
|
802
|
-
const updates = await this.getUpdates();
|
|
803
|
-
for (const update of updates) {
|
|
804
|
-
await this.handleUpdate(update);
|
|
805
|
-
}
|
|
806
|
-
} catch (err) {
|
|
807
|
-
if (!this.running) break;
|
|
808
|
-
console.error(`[telegram] Poll error: ${err instanceof Error ? err.message : err}`);
|
|
809
|
-
await sleep(3e3);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
/** Stop long polling. */
|
|
814
|
-
stop() {
|
|
815
|
-
this.running = false;
|
|
816
|
-
}
|
|
817
|
-
/** Send a text message to a specific chat. Converts markdown and splits long messages. */
|
|
818
|
-
async send(chatId, text, buttons) {
|
|
819
|
-
const converted = toTelegramMarkdown(text);
|
|
820
|
-
const chunks = splitMessage(converted);
|
|
821
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
822
|
-
const isLast = i === chunks.length - 1;
|
|
823
|
-
const replyMarkup = isLast && buttons ? {
|
|
824
|
-
inline_keyboard: buttons.map(
|
|
825
|
-
(row) => row.map((b) => ({ text: b.text, callback_data: b.callback_data }))
|
|
826
|
-
)
|
|
827
|
-
} : void 0;
|
|
828
|
-
await this.apiCall("sendMessage", {
|
|
829
|
-
chat_id: chatId,
|
|
830
|
-
text: chunks[i],
|
|
831
|
-
parse_mode: "Markdown",
|
|
832
|
-
...replyMarkup ? { reply_markup: replyMarkup } : {}
|
|
833
|
-
});
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
/** Send a plain text message (no markdown parsing) to a specific chat. */
|
|
837
|
-
async sendPlain(chatId, text) {
|
|
838
|
-
const chunks = splitMessage(text);
|
|
839
|
-
for (const chunk of chunks) {
|
|
840
|
-
await this.apiCall("sendMessage", {
|
|
841
|
-
chat_id: chatId,
|
|
842
|
-
text: chunk
|
|
843
|
-
});
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
/** Send a typing indicator to a specific chat. */
|
|
847
|
-
async sendTyping(chatId) {
|
|
848
|
-
await this.apiCall("sendChatAction", {
|
|
849
|
-
chat_id: chatId,
|
|
850
|
-
action: "typing"
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
/** Get a direct download URL for a Telegram file. */
|
|
854
|
-
async getFileUrl(fileId) {
|
|
855
|
-
const result = await this.apiCall("getFile", { file_id: fileId });
|
|
856
|
-
if (!result.ok) throw new Error(`Failed to get file: ${JSON.stringify(result)}`);
|
|
857
|
-
const filePath = result.result.file_path;
|
|
858
|
-
return `${TELEGRAM_API}/file/bot${this.token}/${filePath}`;
|
|
859
|
-
}
|
|
860
|
-
// ── Private ───────────────────────────────────────────
|
|
861
|
-
async getUpdates() {
|
|
862
|
-
const result = await this.apiCall("getUpdates", {
|
|
863
|
-
offset: this.offset,
|
|
864
|
-
timeout: 30,
|
|
865
|
-
allowed_updates: ["message", "callback_query", "my_chat_member"]
|
|
866
|
-
});
|
|
867
|
-
if (!result.ok || !Array.isArray(result.result)) return [];
|
|
868
|
-
const updates = result.result;
|
|
869
|
-
if (updates.length > 0) {
|
|
870
|
-
this.offset = updates[updates.length - 1].update_id + 1;
|
|
871
|
-
}
|
|
872
|
-
return updates;
|
|
873
|
-
}
|
|
874
|
-
async handleUpdate(update) {
|
|
875
|
-
if (update.message) {
|
|
876
|
-
const msg = update.message;
|
|
877
|
-
if (msg.from.id !== this.allowedUserId) {
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
if (msg.text && this.onMessage) {
|
|
881
|
-
this.onMessage({
|
|
882
|
-
text: msg.text,
|
|
883
|
-
chatId: msg.chat.id,
|
|
884
|
-
chatType: msg.chat.type,
|
|
885
|
-
chatTitle: msg.chat.title
|
|
886
|
-
});
|
|
887
|
-
} else if (msg.voice && this.onVoiceMessage) {
|
|
888
|
-
this.onVoiceMessage({
|
|
889
|
-
fileId: msg.voice.file_id,
|
|
890
|
-
duration: msg.voice.duration,
|
|
891
|
-
chatId: msg.chat.id,
|
|
892
|
-
chatType: msg.chat.type,
|
|
893
|
-
chatTitle: msg.chat.title
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
if (update.my_chat_member) {
|
|
898
|
-
const member = update.my_chat_member;
|
|
899
|
-
const status = member.new_chat_member.status;
|
|
900
|
-
if ((status === "member" || status === "administrator") && this.onBotAdded) {
|
|
901
|
-
this.onBotAdded(member.chat.id, member.chat.title);
|
|
902
|
-
} else if ((status === "left" || status === "kicked") && this.onBotRemoved) {
|
|
903
|
-
this.onBotRemoved(member.chat.id);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
if (update.callback_query) {
|
|
907
|
-
const cb = update.callback_query;
|
|
908
|
-
if (cb.from.id !== this.allowedUserId) return;
|
|
909
|
-
await this.apiCall("answerCallbackQuery", { callback_query_id: cb.id });
|
|
910
|
-
if (cb.data && this.onCallback) {
|
|
911
|
-
this.onCallback(cb.data, cb.message.chat.id);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
async apiCall(method, body) {
|
|
916
|
-
const url = `${TELEGRAM_API}/bot${this.token}/${method}`;
|
|
917
|
-
const response = await fetch(url, {
|
|
918
|
-
method: "POST",
|
|
919
|
-
headers: { "Content-Type": "application/json" },
|
|
920
|
-
body: body ? JSON.stringify(body) : void 0
|
|
921
|
-
});
|
|
922
|
-
if (!response.ok) {
|
|
923
|
-
return { ok: false };
|
|
924
|
-
}
|
|
925
|
-
return response.json();
|
|
926
|
-
}
|
|
927
|
-
};
|
|
928
|
-
function toTelegramMarkdown(text) {
|
|
929
|
-
const lines = text.split("\n");
|
|
930
|
-
const result = [];
|
|
931
|
-
let inCodeBlock = false;
|
|
932
|
-
for (const line of lines) {
|
|
933
|
-
if (line.trimStart().startsWith("```")) {
|
|
934
|
-
inCodeBlock = !inCodeBlock;
|
|
935
|
-
result.push(line);
|
|
936
|
-
continue;
|
|
937
|
-
}
|
|
938
|
-
if (inCodeBlock) {
|
|
939
|
-
result.push(line);
|
|
940
|
-
continue;
|
|
941
|
-
}
|
|
942
|
-
let transformed = line;
|
|
943
|
-
const headingMatch = transformed.match(/^(#{1,6})\s+(.+)$/);
|
|
944
|
-
if (headingMatch) {
|
|
945
|
-
transformed = `*${headingMatch[2]}*`;
|
|
946
|
-
result.push(transformed);
|
|
947
|
-
continue;
|
|
948
|
-
}
|
|
949
|
-
if (/^(-{3,}|_{3,}|\*{3,})$/.test(transformed.trim())) {
|
|
950
|
-
result.push("");
|
|
951
|
-
continue;
|
|
952
|
-
}
|
|
953
|
-
transformed = transformed.replace(/\*\*(.+?)\*\*/g, "*$1*");
|
|
954
|
-
result.push(transformed);
|
|
955
|
-
}
|
|
956
|
-
return result.join("\n");
|
|
957
|
-
}
|
|
958
|
-
function splitMessage(text) {
|
|
959
|
-
if (text.length <= MAX_MESSAGE_LENGTH) return [text];
|
|
960
|
-
const chunks = [];
|
|
961
|
-
let remaining = text;
|
|
962
|
-
while (remaining.length > 0) {
|
|
963
|
-
if (remaining.length <= MAX_MESSAGE_LENGTH) {
|
|
964
|
-
chunks.push(remaining);
|
|
965
|
-
break;
|
|
966
|
-
}
|
|
967
|
-
let splitAt = remaining.lastIndexOf("\n", MAX_MESSAGE_LENGTH);
|
|
968
|
-
if (splitAt === -1 || splitAt < MAX_MESSAGE_LENGTH * 0.5) {
|
|
969
|
-
splitAt = remaining.lastIndexOf(" ", MAX_MESSAGE_LENGTH);
|
|
970
|
-
}
|
|
971
|
-
if (splitAt === -1 || splitAt < MAX_MESSAGE_LENGTH * 0.5) {
|
|
972
|
-
splitAt = MAX_MESSAGE_LENGTH;
|
|
973
|
-
}
|
|
974
|
-
chunks.push(remaining.slice(0, splitAt));
|
|
975
|
-
remaining = remaining.slice(splitAt).trimStart();
|
|
976
|
-
}
|
|
977
|
-
return chunks;
|
|
978
|
-
}
|
|
979
|
-
function sleep(ms) {
|
|
980
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
// src/serve-mode.ts
|
|
984
681
|
function getTelegramConfigPath() {
|
|
985
682
|
return path3.join(getAppPaths().agentDir, "boss", "telegram.json");
|
|
986
683
|
}
|
|
@@ -1001,8 +698,14 @@ async function saveBossTelegramConfig(config) {
|
|
|
1001
698
|
}
|
|
1002
699
|
function formatItemForTelegram(item) {
|
|
1003
700
|
switch (item.kind) {
|
|
701
|
+
case "banner":
|
|
1004
702
|
case "user":
|
|
1005
|
-
case "
|
|
703
|
+
case "tool_start":
|
|
704
|
+
case "tool_done":
|
|
705
|
+
case "tool_group":
|
|
706
|
+
case "compacting":
|
|
707
|
+
case "compacted":
|
|
708
|
+
case "stopped":
|
|
1006
709
|
case "worker_event":
|
|
1007
710
|
return null;
|
|
1008
711
|
case "assistant": {
|
|
@@ -1533,7 +1236,7 @@ function printSetupBanner() {
|
|
|
1533
1236
|
|
|
1534
1237
|
// src/orchestrator-app.tsx
|
|
1535
1238
|
init_esm_shims();
|
|
1536
|
-
var
|
|
1239
|
+
var import_react14 = __toESM(require_react(), 1);
|
|
1537
1240
|
|
|
1538
1241
|
// ../ggcoder/dist/ui/components/index.js
|
|
1539
1242
|
init_esm_shims();
|
|
@@ -1543,10 +1246,66 @@ init_esm_shims();
|
|
|
1543
1246
|
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
1544
1247
|
var import_react3 = __toESM(require_react(), 1);
|
|
1545
1248
|
|
|
1546
|
-
// ../ggcoder/dist/ui/components/
|
|
1249
|
+
// ../ggcoder/dist/ui/components/SelectList.js
|
|
1547
1250
|
init_esm_shims();
|
|
1548
1251
|
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
|
|
1549
1252
|
var import_react4 = __toESM(require_react(), 1);
|
|
1253
|
+
function SelectList({ items, onSelect, onCancel, initialIndex = 0, windowSize }) {
|
|
1254
|
+
const theme = useTheme();
|
|
1255
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react4.useState)(initialIndex);
|
|
1256
|
+
const [filter, setFilter] = (0, import_react4.useState)("");
|
|
1257
|
+
const filtered = (0, import_react4.useMemo)(() => {
|
|
1258
|
+
if (!filter)
|
|
1259
|
+
return items;
|
|
1260
|
+
const lower = filter.toLowerCase();
|
|
1261
|
+
return items.filter((item) => item.label.toLowerCase().includes(lower) || item.value.toLowerCase().includes(lower));
|
|
1262
|
+
}, [items, filter]);
|
|
1263
|
+
use_input_default((input, key) => {
|
|
1264
|
+
const inputWithoutFocusReports = stripTerminalFocusSequences(input);
|
|
1265
|
+
if (!inputWithoutFocusReports && input)
|
|
1266
|
+
return;
|
|
1267
|
+
input = inputWithoutFocusReports;
|
|
1268
|
+
if (key.escape) {
|
|
1269
|
+
onCancel();
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
if (key.return) {
|
|
1273
|
+
if (filtered.length > 0) {
|
|
1274
|
+
onSelect(filtered[selectedIndex].value);
|
|
1275
|
+
}
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
if (key.upArrow) {
|
|
1279
|
+
setSelectedIndex((i) => Math.max(0, i - 1));
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (key.downArrow) {
|
|
1283
|
+
setSelectedIndex((i) => Math.min(filtered.length - 1, i + 1));
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
if (key.backspace || key.delete) {
|
|
1287
|
+
setFilter((f) => f.slice(0, -1));
|
|
1288
|
+
setSelectedIndex(0);
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
if (input && !key.ctrl && !key.meta) {
|
|
1292
|
+
setFilter((f) => f + input);
|
|
1293
|
+
setSelectedIndex(0);
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
const total = filtered.length;
|
|
1297
|
+
const clampedIndex = Math.min(Math.max(selectedIndex, 0), Math.max(0, total - 1));
|
|
1298
|
+
const useWindow = windowSize !== void 0 && windowSize > 0 && total > windowSize;
|
|
1299
|
+
const start = useWindow ? Math.max(0, Math.min(clampedIndex - Math.floor(windowSize / 2), total - windowSize)) : 0;
|
|
1300
|
+
const end = useWindow ? Math.min(start + windowSize, total) : total;
|
|
1301
|
+
const visible = useWindow ? filtered.slice(start, end) : filtered;
|
|
1302
|
+
const hasAbove = useWindow && start > 0;
|
|
1303
|
+
const hasBelow = useWindow && end < total;
|
|
1304
|
+
return (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", children: [filter && (0, import_jsx_runtime4.jsx)(Box_default, { marginBottom: 1, children: (0, import_jsx_runtime4.jsxs)(Text, { color: theme.textDim, children: ["Filter: ", filter] }) }), hasAbove && (0, import_jsx_runtime4.jsxs)(Text, { color: theme.textDim, children: [" \u2191 ", start, " more"] }), visible.map((item, i) => {
|
|
1305
|
+
const index = useWindow ? start + i : i;
|
|
1306
|
+
return (0, import_jsx_runtime4.jsxs)(Box_default, { children: [(0, import_jsx_runtime4.jsxs)(Text, { color: index === clampedIndex ? theme.primary : theme.text, children: [index === clampedIndex ? "\u276F " : " ", item.label] }), item.description && (0, import_jsx_runtime4.jsxs)(Text, { color: theme.textDim, children: [" \u2014 ", item.description] })] }, item.value);
|
|
1307
|
+
}), hasBelow && (0, import_jsx_runtime4.jsxs)(Text, { color: theme.textDim, children: [" \u2193 ", total - end, " more"] }), filtered.length === 0 && (0, import_jsx_runtime4.jsx)(Text, { color: theme.textDim, children: "No matches" }), (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: (0, import_jsx_runtime4.jsx)(Text, { color: theme.textDim, children: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc cancel" }) })] });
|
|
1308
|
+
}
|
|
1550
1309
|
|
|
1551
1310
|
// ../ggcoder/dist/ui/components/SessionSelector.js
|
|
1552
1311
|
init_esm_shims();
|
|
@@ -1558,14 +1317,19 @@ init_esm_shims();
|
|
|
1558
1317
|
var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
|
|
1559
1318
|
var import_react6 = __toESM(require_react(), 1);
|
|
1560
1319
|
|
|
1320
|
+
// src/boss-chat-screen.tsx
|
|
1321
|
+
init_esm_shims();
|
|
1322
|
+
var import_react12 = __toESM(require_react(), 1);
|
|
1323
|
+
|
|
1561
1324
|
// src/boss-footer.tsx
|
|
1562
1325
|
init_esm_shims();
|
|
1563
1326
|
var import_react7 = __toESM(require_react(), 1);
|
|
1564
1327
|
var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
|
|
1565
1328
|
var PARTIAL_BLOCKS = [" ", "\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u258A", "\u2589", "\u2588"];
|
|
1566
1329
|
var LIGHT_SHADE = "\u2591";
|
|
1330
|
+
var BAR_WIDTH = 8;
|
|
1567
1331
|
var SHORT_MODELS = {
|
|
1568
|
-
"claude-opus-4-
|
|
1332
|
+
"claude-opus-4-8": "Opus",
|
|
1569
1333
|
"claude-sonnet-4-6": "Sonnet",
|
|
1570
1334
|
"claude-haiku-4-5": "Haiku",
|
|
1571
1335
|
"claude-haiku-4-5-20251001": "Haiku",
|
|
@@ -1577,17 +1341,51 @@ var SHORT_MODELS = {
|
|
|
1577
1341
|
function shortModel(model) {
|
|
1578
1342
|
return SHORT_MODELS[model] ?? model;
|
|
1579
1343
|
}
|
|
1580
|
-
function
|
|
1344
|
+
function getBossFooterContextPercent(model, tokensIn) {
|
|
1581
1345
|
const limit = getContextWindow(model);
|
|
1582
1346
|
if (!limit || tokensIn === 0) return 0;
|
|
1583
1347
|
return Math.round(tokensIn / limit * 100);
|
|
1584
1348
|
}
|
|
1349
|
+
function getContextColor(pct, theme) {
|
|
1350
|
+
if (pct >= 80) return theme.error;
|
|
1351
|
+
if (pct >= 50) return theme.warning;
|
|
1352
|
+
return theme.success;
|
|
1353
|
+
}
|
|
1354
|
+
function getThinkingColor(level, theme) {
|
|
1355
|
+
if (!level) return theme.textDim;
|
|
1356
|
+
if (level === "low") return theme.textMuted;
|
|
1357
|
+
if (level === "medium") return theme.accent;
|
|
1358
|
+
if (level === "high") return theme.warning;
|
|
1359
|
+
return COLORS.accent;
|
|
1360
|
+
}
|
|
1361
|
+
function getBossFooterThinkingLabel(level) {
|
|
1362
|
+
return level ? `Thinking ${level}` : "Thinking off";
|
|
1363
|
+
}
|
|
1585
1364
|
var SHORT_RADIO = {
|
|
1586
1365
|
"somafm-groove-salad": "Groove Salad",
|
|
1587
1366
|
"somafm-drone-zone": "Drone Zone",
|
|
1588
1367
|
"radio-paradise": "Radio Paradise",
|
|
1589
1368
|
"george-fm": "George FM"
|
|
1590
1369
|
};
|
|
1370
|
+
function renderContextBar({
|
|
1371
|
+
contextPct,
|
|
1372
|
+
contextColor,
|
|
1373
|
+
dimColor
|
|
1374
|
+
}) {
|
|
1375
|
+
const fillFloat = Math.min(contextPct / 100 * BAR_WIDTH, BAR_WIDTH);
|
|
1376
|
+
const barChars = [];
|
|
1377
|
+
for (let i = 0; i < BAR_WIDTH; i++) {
|
|
1378
|
+
const cellFill = Math.max(0, Math.min(1, fillFloat - i));
|
|
1379
|
+
const eighths = Math.round(cellFill * 8);
|
|
1380
|
+
barChars.push(
|
|
1381
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: eighths > 0 ? contextColor : dimColor, children: eighths > 0 ? PARTIAL_BLOCKS[eighths] : LIGHT_SHADE }, i)
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
return barChars;
|
|
1385
|
+
}
|
|
1386
|
+
function getBossFooterScopeLabel(scope) {
|
|
1387
|
+
return scope === "all" ? "all projects" : scope;
|
|
1388
|
+
}
|
|
1591
1389
|
function BossFooter({
|
|
1592
1390
|
bossModel,
|
|
1593
1391
|
workerModel,
|
|
@@ -1595,149 +1393,231 @@ function BossFooter({
|
|
|
1595
1393
|
exitPending,
|
|
1596
1394
|
bossThinkingLevel,
|
|
1597
1395
|
updatePending,
|
|
1598
|
-
currentRadioStationId
|
|
1396
|
+
currentRadioStationId,
|
|
1397
|
+
scope
|
|
1599
1398
|
}) {
|
|
1600
1399
|
const theme = useTheme();
|
|
1601
1400
|
const { columns } = useTerminalSize();
|
|
1602
1401
|
if (exitPending) {
|
|
1603
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, {
|
|
1402
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { paddingLeft: 1, paddingRight: 1, width: columns, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.warning, children: "Press Ctrl+C again to exit" }) });
|
|
1604
1403
|
}
|
|
1605
|
-
const contextPct =
|
|
1606
|
-
const contextColor = contextPct
|
|
1404
|
+
const contextPct = getBossFooterContextPercent(bossModel, tokensIn);
|
|
1405
|
+
const contextColor = getContextColor(contextPct, theme);
|
|
1607
1406
|
const sep = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.border, children: " \u2502 " });
|
|
1608
|
-
const
|
|
1609
|
-
const
|
|
1610
|
-
const
|
|
1611
|
-
for (let i = 0; i < barWidth; i++) {
|
|
1612
|
-
const cellFill = Math.max(0, Math.min(1, fillFloat - i));
|
|
1613
|
-
const eighths = Math.round(cellFill * 8);
|
|
1614
|
-
if (eighths === 8) {
|
|
1615
|
-
barChars.push(
|
|
1616
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: contextColor, children: PARTIAL_BLOCKS[8] }, i)
|
|
1617
|
-
);
|
|
1618
|
-
} else if (eighths > 0) {
|
|
1619
|
-
barChars.push(
|
|
1620
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: contextColor, children: PARTIAL_BLOCKS[eighths] }, i)
|
|
1621
|
-
);
|
|
1622
|
-
} else {
|
|
1623
|
-
barChars.push(
|
|
1624
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.textDim, children: LIGHT_SHADE }, i)
|
|
1625
|
-
);
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1407
|
+
const bossName = shortModel(bossModel);
|
|
1408
|
+
const workerName = shortModel(workerModel);
|
|
1409
|
+
const thinkingText = getBossFooterThinkingLabel(bossThinkingLevel);
|
|
1628
1410
|
const radioName = currentRadioStationId ? SHORT_RADIO[currentRadioStationId] ?? currentRadioStationId : null;
|
|
1629
|
-
const
|
|
1630
|
-
const
|
|
1631
|
-
const
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
(
|
|
1637
|
-
const
|
|
1638
|
-
const
|
|
1639
|
-
const
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1411
|
+
const updateText = updatePending ? "Update ready. Restart GG Boss." : null;
|
|
1412
|
+
const leftText = getBossFooterScopeLabel(scope);
|
|
1413
|
+
const barChars = renderContextBar({
|
|
1414
|
+
contextPct,
|
|
1415
|
+
contextColor,
|
|
1416
|
+
dimColor: theme.textDim
|
|
1417
|
+
});
|
|
1418
|
+
const rightLen = BAR_WIDTH + 1 + String(contextPct).length + 3 + bossName.length + 3 + "workers ".length + workerName.length + 3 + thinkingText.length + (radioName ? 3 + 2 + radioName.length : 0) + (updateText ? 3 + updateText.length : 0);
|
|
1419
|
+
const availableWidth = columns - 2;
|
|
1420
|
+
const fitsOnOneLine = leftText.length + rightLen <= availableWidth;
|
|
1421
|
+
const hideRadio = !!radioName && leftText.length + rightLen > availableWidth + 8;
|
|
1422
|
+
const compactUpdate = !!updateText && leftText.length + rightLen > availableWidth + 12;
|
|
1423
|
+
const rightContent = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1424
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: barChars }),
|
|
1425
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: contextColor, children: [
|
|
1426
|
+
" ",
|
|
1427
|
+
contextPct,
|
|
1428
|
+
"%"
|
|
1429
|
+
] }),
|
|
1430
|
+
sep,
|
|
1431
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.primary, bold: true, children: bossName }),
|
|
1432
|
+
sep,
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.textDim, children: "workers " }),
|
|
1434
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: COLORS.accent, bold: true, children: workerName }),
|
|
1435
|
+
sep,
|
|
1436
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: getThinkingColor(bossThinkingLevel, theme), bold: bossThinkingLevel === "high", children: thinkingText }),
|
|
1437
|
+
radioName && !hideRadio && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1652
1438
|
sep,
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
sep,
|
|
1657
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: bossThinkingLevel ? theme.accent : theme.textDim, children: bossThinkingLevel ? "Thinking on" : "Thinking off" })
|
|
1658
|
-
] }),
|
|
1659
|
-
radioName && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1660
|
-
sep,
|
|
1661
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: theme.secondary ?? theme.accent, children: [
|
|
1662
|
-
"\u266A ",
|
|
1663
|
-
radioName
|
|
1664
|
-
] })
|
|
1665
|
-
] }),
|
|
1666
|
-
updatePending && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1667
|
-
sep,
|
|
1668
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.success, bold: true, wrap: "truncate", children: useShortUpdate ? "Update ready" : "Update ready. Restart GG Boss." })
|
|
1439
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: theme.secondary, children: [
|
|
1440
|
+
"\u266A ",
|
|
1441
|
+
radioName
|
|
1669
1442
|
] })
|
|
1443
|
+
] }),
|
|
1444
|
+
updateText && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1445
|
+
sep,
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.success, bold: true, wrap: "truncate", children: compactUpdate ? "Update ready" : updateText })
|
|
1670
1447
|
] })
|
|
1671
1448
|
] });
|
|
1449
|
+
if (fitsOnOneLine) {
|
|
1450
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { paddingLeft: 1, paddingRight: 1, width: columns, children: [
|
|
1451
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.textDim, wrap: "truncate", children: leftText }) }),
|
|
1452
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { flexShrink: 0, children: rightContent })
|
|
1453
|
+
] });
|
|
1454
|
+
}
|
|
1455
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, width: columns, children: [
|
|
1456
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.textDim, wrap: "truncate", children: leftText }) }),
|
|
1457
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { children: rightContent })
|
|
1458
|
+
] });
|
|
1672
1459
|
}
|
|
1673
1460
|
|
|
1674
|
-
// src/
|
|
1461
|
+
// src/boss-model-selector.tsx
|
|
1675
1462
|
init_esm_shims();
|
|
1676
|
-
var
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
if (
|
|
1463
|
+
var import_react8 = __toESM(require_react(), 1);
|
|
1464
|
+
var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
|
|
1465
|
+
var MAX_MODELS_TO_SHOW = 8;
|
|
1466
|
+
var PROVIDER_LABEL = {
|
|
1467
|
+
anthropic: "Anthropic",
|
|
1468
|
+
openai: "OpenAI",
|
|
1469
|
+
gemini: "Gemini",
|
|
1470
|
+
glm: "Z.AI",
|
|
1471
|
+
moonshot: "Moonshot",
|
|
1472
|
+
xiaomi: "Xiaomi",
|
|
1473
|
+
minimax: "MiniMax",
|
|
1474
|
+
deepseek: "DeepSeek",
|
|
1475
|
+
openrouter: "OpenRouter"
|
|
1476
|
+
};
|
|
1477
|
+
var ESC = String.fromCharCode(27);
|
|
1478
|
+
var ESC_FOCUS_GAINED = `${ESC}[I`;
|
|
1479
|
+
var ESC_FOCUS_LOST = `${ESC}[O`;
|
|
1480
|
+
var ESC_LESS_FOCUS_GAINED = "[I";
|
|
1481
|
+
var ESC_LESS_FOCUS_LOST = "[O";
|
|
1482
|
+
function stripTerminalFocusSequences2(input) {
|
|
1483
|
+
const withoutEscFocusReports = input.replaceAll(ESC_FOCUS_GAINED, "").replaceAll(ESC_FOCUS_LOST, "");
|
|
1484
|
+
let remaining = withoutEscFocusReports;
|
|
1485
|
+
while (remaining.length > 0) {
|
|
1486
|
+
if (remaining.startsWith(ESC_LESS_FOCUS_GAINED) || remaining.startsWith(ESC_LESS_FOCUS_LOST)) {
|
|
1487
|
+
remaining = remaining.slice(2);
|
|
1488
|
+
continue;
|
|
1489
|
+
}
|
|
1490
|
+
return withoutEscFocusReports;
|
|
1700
1491
|
}
|
|
1701
|
-
return
|
|
1492
|
+
return "";
|
|
1702
1493
|
}
|
|
1703
|
-
function
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1494
|
+
function BossModelSelectList({
|
|
1495
|
+
items,
|
|
1496
|
+
onSelect,
|
|
1497
|
+
onCancel,
|
|
1498
|
+
initialIndex
|
|
1499
|
+
}) {
|
|
1500
|
+
const theme = useTheme();
|
|
1501
|
+
const { columns } = useTerminalSize();
|
|
1502
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react8.useState)(initialIndex);
|
|
1503
|
+
const [filter, setFilter] = (0, import_react8.useState)("");
|
|
1504
|
+
const filtered = (0, import_react8.useMemo)(() => {
|
|
1505
|
+
if (!filter) return items;
|
|
1506
|
+
const lower = filter.toLowerCase();
|
|
1507
|
+
return items.filter(
|
|
1508
|
+
(item) => item.label.toLowerCase().includes(lower) || item.value.toLowerCase().includes(lower) || item.description.toLowerCase().includes(lower)
|
|
1509
|
+
);
|
|
1510
|
+
}, [items, filter]);
|
|
1511
|
+
use_input_default((input, key) => {
|
|
1512
|
+
const inputWithoutFocusReports = stripTerminalFocusSequences2(input);
|
|
1513
|
+
if (!inputWithoutFocusReports && input) return;
|
|
1514
|
+
input = inputWithoutFocusReports;
|
|
1515
|
+
if (key.escape) {
|
|
1516
|
+
onCancel();
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
if (key.return) {
|
|
1520
|
+
const selected = filtered[selectedIndex];
|
|
1521
|
+
if (selected) onSelect(selected.value);
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
if (key.upArrow) {
|
|
1525
|
+
setSelectedIndex((i) => Math.max(0, i - 1));
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
if (key.downArrow) {
|
|
1529
|
+
setSelectedIndex((i) => filtered.length === 0 ? 0 : Math.min(filtered.length - 1, i + 1));
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
if (key.backspace || key.delete) {
|
|
1533
|
+
setFilter((current) => current.slice(0, -1));
|
|
1534
|
+
setSelectedIndex(0);
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
if (input && !key.ctrl && !key.meta) {
|
|
1538
|
+
setFilter((current) => current + input);
|
|
1539
|
+
setSelectedIndex(0);
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
const total = filtered.length;
|
|
1543
|
+
const idx = Math.min(Math.max(selectedIndex, 0), Math.max(0, total - 1));
|
|
1544
|
+
const start = total <= MAX_MODELS_TO_SHOW ? 0 : Math.max(0, Math.min(idx - Math.floor(MAX_MODELS_TO_SHOW / 2), total - MAX_MODELS_TO_SHOW));
|
|
1545
|
+
const end = Math.min(start + MAX_MODELS_TO_SHOW, total);
|
|
1546
|
+
const visible = filtered.slice(start, end);
|
|
1547
|
+
const width = Math.max(20, columns);
|
|
1548
|
+
const maxLabelLength = Math.max(0, ...filtered.map((item) => item.label.length));
|
|
1549
|
+
const labelColumnWidth = Math.min(maxLabelLength, Math.floor(width * 0.5));
|
|
1550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, width, children: [
|
|
1551
|
+
filter && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.textDim, children: [
|
|
1552
|
+
"Filter: ",
|
|
1553
|
+
filter
|
|
1554
|
+
] }),
|
|
1555
|
+
start > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.text, children: "\u25B2" }),
|
|
1556
|
+
visible.map((item, i) => {
|
|
1557
|
+
const actualIndex = start + i;
|
|
1558
|
+
const isSelected = actualIndex === idx;
|
|
1559
|
+
const textColor = isSelected ? theme.commandColor : theme.textDim;
|
|
1560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1561
|
+
Box_default,
|
|
1562
|
+
{
|
|
1563
|
+
flexDirection: "row",
|
|
1564
|
+
backgroundColor: isSelected ? theme.border : void 0,
|
|
1565
|
+
children: [
|
|
1566
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { width: labelColumnWidth, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: textColor, children: item.label }) }),
|
|
1567
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { flexGrow: 1, paddingLeft: 3, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: textColor, wrap: "truncate", children: item.description.slice(0, 100) }) })
|
|
1568
|
+
]
|
|
1569
|
+
},
|
|
1570
|
+
item.value
|
|
1571
|
+
);
|
|
1572
|
+
}),
|
|
1573
|
+
end < total && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: "\u25BC" }),
|
|
1574
|
+
total > MAX_MODELS_TO_SHOW && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.textDim, children: [
|
|
1575
|
+
"(",
|
|
1576
|
+
idx + 1,
|
|
1577
|
+
"/",
|
|
1578
|
+
total,
|
|
1579
|
+
")"
|
|
1580
|
+
] }),
|
|
1581
|
+
total === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: theme.textDim, children: "No matches" })
|
|
1582
|
+
] });
|
|
1583
|
+
}
|
|
1584
|
+
function BossModelSelector({
|
|
1585
|
+
onSelect,
|
|
1586
|
+
onCancel,
|
|
1587
|
+
currentModel,
|
|
1588
|
+
currentProvider
|
|
1589
|
+
}) {
|
|
1590
|
+
const currentValue = `${currentProvider}:${currentModel}`;
|
|
1591
|
+
const items = (0, import_react8.useMemo)(
|
|
1592
|
+
() => MODELS.map((model) => {
|
|
1593
|
+
const value = `${model.provider}:${model.id}`;
|
|
1594
|
+
const isCurrent = value === currentValue;
|
|
1595
|
+
return {
|
|
1596
|
+
label: `${isCurrent ? "* " : " "}${model.name}`,
|
|
1597
|
+
value,
|
|
1598
|
+
description: `${PROVIDER_LABEL[model.provider] ?? model.provider} \xB7 ${model.id}`
|
|
1599
|
+
};
|
|
1600
|
+
}),
|
|
1601
|
+
[currentValue]
|
|
1602
|
+
);
|
|
1603
|
+
const initialIndex = Math.max(
|
|
1604
|
+
0,
|
|
1605
|
+
items.findIndex((item) => item.value === currentValue)
|
|
1606
|
+
);
|
|
1607
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1608
|
+
BossModelSelectList,
|
|
1609
|
+
{
|
|
1610
|
+
items,
|
|
1611
|
+
onSelect,
|
|
1612
|
+
onCancel,
|
|
1613
|
+
initialIndex
|
|
1614
|
+
}
|
|
1615
|
+
);
|
|
1737
1616
|
}
|
|
1738
1617
|
|
|
1739
|
-
// src/
|
|
1618
|
+
// src/boss-tasks-overlay.tsx
|
|
1740
1619
|
init_esm_shims();
|
|
1620
|
+
var import_react9 = __toESM(require_react(), 1);
|
|
1741
1621
|
|
|
1742
1622
|
// src/colors.ts
|
|
1743
1623
|
init_esm_shims();
|
|
@@ -1770,166 +1650,8 @@ function projectColor(name) {
|
|
|
1770
1650
|
return PROJECT_COLORS[stableHash(name) % PROJECT_COLORS.length];
|
|
1771
1651
|
}
|
|
1772
1652
|
|
|
1773
|
-
// src/tool-formatters.ts
|
|
1774
|
-
function truncate2(s, max) {
|
|
1775
|
-
if (max <= 1) return "\u2026";
|
|
1776
|
-
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
1777
|
-
}
|
|
1778
|
-
function promptWorkerDetailLen(project) {
|
|
1779
|
-
const cols = process.stdout.columns ?? 80;
|
|
1780
|
-
const fixed = 2 + 13 + 1 + project.length + 3 + 1 + 1 + 11 + 6;
|
|
1781
|
-
return Math.max(20, cols - fixed);
|
|
1782
|
-
}
|
|
1783
|
-
var bossToolFormatters = {
|
|
1784
|
-
formatLabel(name) {
|
|
1785
|
-
switch (name) {
|
|
1786
|
-
case "list_workers":
|
|
1787
|
-
return "List Workers";
|
|
1788
|
-
case "get_worker_status":
|
|
1789
|
-
return "Worker Status";
|
|
1790
|
-
case "prompt_worker":
|
|
1791
|
-
return "Prompt Worker";
|
|
1792
|
-
case "get_worker_summary":
|
|
1793
|
-
return "Worker Summary";
|
|
1794
|
-
default:
|
|
1795
|
-
return void 0;
|
|
1796
|
-
}
|
|
1797
|
-
},
|
|
1798
|
-
formatDetail(name, args) {
|
|
1799
|
-
switch (name) {
|
|
1800
|
-
case "list_workers":
|
|
1801
|
-
return "";
|
|
1802
|
-
case "get_worker_status":
|
|
1803
|
-
case "get_worker_summary":
|
|
1804
|
-
return truncate2(String(args.project ?? ""), 40);
|
|
1805
|
-
case "prompt_worker": {
|
|
1806
|
-
const project = String(args.project ?? "");
|
|
1807
|
-
const message = String(args.message ?? "").replace(/\s+/g, " ");
|
|
1808
|
-
const fresh = args.fresh === true;
|
|
1809
|
-
const maxMsg = promptWorkerDetailLen(project) - (fresh ? 8 : 0);
|
|
1810
|
-
const truncMsg = truncate2(message, Math.max(15, maxMsg));
|
|
1811
|
-
const head = fresh ? "fresh \xB7 " : "";
|
|
1812
|
-
return project ? `${head}${project} \xB7 ${truncMsg}` : `${head}${truncMsg}`;
|
|
1813
|
-
}
|
|
1814
|
-
default:
|
|
1815
|
-
return void 0;
|
|
1816
|
-
}
|
|
1817
|
-
},
|
|
1818
|
-
formatInline(name, result, isError) {
|
|
1819
|
-
if (isError) return void 0;
|
|
1820
|
-
switch (name) {
|
|
1821
|
-
case "list_workers": {
|
|
1822
|
-
const lines = result.split("\n").filter((l) => l.startsWith("-"));
|
|
1823
|
-
return `${lines.length} worker${lines.length === 1 ? "" : "s"}`;
|
|
1824
|
-
}
|
|
1825
|
-
case "prompt_worker": {
|
|
1826
|
-
if (result.includes("currently working")) {
|
|
1827
|
-
return { text: "busy \u2014 skipped", color: "#fbbf24" };
|
|
1828
|
-
}
|
|
1829
|
-
if (result.includes("Unknown project")) {
|
|
1830
|
-
return { text: "unknown project", color: "#f87171" };
|
|
1831
|
-
}
|
|
1832
|
-
const project = String(result.match(/"([^"]+)"/)?.[1] ?? "");
|
|
1833
|
-
const color = project ? projectColor(project) : "#e11d48";
|
|
1834
|
-
return { text: "dispatched", color };
|
|
1835
|
-
}
|
|
1836
|
-
case "get_worker_status": {
|
|
1837
|
-
const parts = result.split(":");
|
|
1838
|
-
if (parts.length < 2) return void 0;
|
|
1839
|
-
const status = parts.slice(1).join(":").trim();
|
|
1840
|
-
const project = parts[0].trim();
|
|
1841
|
-
return { text: status, color: projectColor(project) };
|
|
1842
|
-
}
|
|
1843
|
-
case "get_worker_summary": {
|
|
1844
|
-
const turnMatch = result.match(/Turn:\s*(\d+)/);
|
|
1845
|
-
const projectMatch = result.match(/Project:\s*(.+)/);
|
|
1846
|
-
const toolsMatch = result.match(/Tools used:\s*(.+)/);
|
|
1847
|
-
const tools = toolsMatch ? toolsMatch[1] : "";
|
|
1848
|
-
const toolCount = tools && tools !== "(no tools used)" ? tools.split(",").length : 0;
|
|
1849
|
-
const turn = turnMatch ? `turn ${turnMatch[1]}` : void 0;
|
|
1850
|
-
const tCount = toolCount > 0 ? `${toolCount} tool${toolCount === 1 ? "" : "s"}` : void 0;
|
|
1851
|
-
const summary = [turn, tCount].filter(Boolean).join(" \xB7 ");
|
|
1852
|
-
if (!summary) return void 0;
|
|
1853
|
-
const project = projectMatch ? projectMatch[1].trim() : "";
|
|
1854
|
-
return project ? { text: summary, color: projectColor(project) } : { text: summary, color: "#9ca3af" };
|
|
1855
|
-
}
|
|
1856
|
-
default:
|
|
1857
|
-
return void 0;
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
};
|
|
1861
|
-
|
|
1862
|
-
// src/boss-phrases.ts
|
|
1863
|
-
init_esm_shims();
|
|
1864
|
-
var BOSS_PHRASES = {
|
|
1865
|
-
// Generic between-states fallback. Probably never shown but keep for safety.
|
|
1866
|
-
idle: ["Standing by", "Waiting for orders", "On call"],
|
|
1867
|
-
// Boss has issued a request, waiting for the LLM to begin streaming.
|
|
1868
|
-
waiting: [
|
|
1869
|
-
"Briefing",
|
|
1870
|
-
"Reviewing the room",
|
|
1871
|
-
"Triaging",
|
|
1872
|
-
"Lining up the brief",
|
|
1873
|
-
"Surveying projects",
|
|
1874
|
-
"Reading the room",
|
|
1875
|
-
"Picking the right hand",
|
|
1876
|
-
"Marshalling thoughts",
|
|
1877
|
-
"Checking the board",
|
|
1878
|
-
"Sizing up the work"
|
|
1879
|
-
],
|
|
1880
|
-
// LLM is mid-thinking-block (extended reasoning).
|
|
1881
|
-
thinking: [
|
|
1882
|
-
"Strategising",
|
|
1883
|
-
"Plotting next move",
|
|
1884
|
-
"Weighing options",
|
|
1885
|
-
"Reasoning",
|
|
1886
|
-
"Deliberating",
|
|
1887
|
-
"Thinking it through",
|
|
1888
|
-
"Mapping the play",
|
|
1889
|
-
"Considering angles",
|
|
1890
|
-
"Calculating odds",
|
|
1891
|
-
"Drafting the call"
|
|
1892
|
-
],
|
|
1893
|
-
// LLM is streaming text — boss is forming its dispatch / response.
|
|
1894
|
-
generating: [
|
|
1895
|
-
"Drafting",
|
|
1896
|
-
"Composing dispatch",
|
|
1897
|
-
"Writing the brief",
|
|
1898
|
-
"Penning instructions",
|
|
1899
|
-
"Wording it up",
|
|
1900
|
-
"Putting it on paper",
|
|
1901
|
-
"Phrasing the ask",
|
|
1902
|
-
"Forming the directive",
|
|
1903
|
-
"Scripting the plan"
|
|
1904
|
-
],
|
|
1905
|
-
// Boss is invoking a tool — most often prompt_worker.
|
|
1906
|
-
tools: [
|
|
1907
|
-
"Coordinating",
|
|
1908
|
-
"Dispatching",
|
|
1909
|
-
"Routing",
|
|
1910
|
-
"Delegating",
|
|
1911
|
-
"Issuing orders",
|
|
1912
|
-
"Handing off",
|
|
1913
|
-
"Aligning workers",
|
|
1914
|
-
"Conducting",
|
|
1915
|
-
"Calling the team",
|
|
1916
|
-
"Steering",
|
|
1917
|
-
"Pulling levers"
|
|
1918
|
-
],
|
|
1919
|
-
// Provider retry (overloaded / rate-limited / etc.).
|
|
1920
|
-
retrying: [
|
|
1921
|
-
"Reattempting",
|
|
1922
|
-
"Course correcting",
|
|
1923
|
-
"Trying again",
|
|
1924
|
-
"Pushing through",
|
|
1925
|
-
"Holding the line"
|
|
1926
|
-
]
|
|
1927
|
-
};
|
|
1928
|
-
|
|
1929
1653
|
// src/boss-tasks-overlay.tsx
|
|
1930
|
-
|
|
1931
|
-
var import_react8 = __toESM(require_react(), 1);
|
|
1932
|
-
var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
|
|
1654
|
+
var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
|
|
1933
1655
|
function statusGlyph(status) {
|
|
1934
1656
|
switch (status) {
|
|
1935
1657
|
case "done":
|
|
@@ -1952,10 +1674,10 @@ function BossTasksOverlay({
|
|
|
1952
1674
|
const theme = useTheme();
|
|
1953
1675
|
const tasksState = useTasksState();
|
|
1954
1676
|
const tasks = tasksState.tasks;
|
|
1955
|
-
const [selectedIndex, setSelectedIndex] = (0,
|
|
1956
|
-
const [status, setStatusMsg] = (0,
|
|
1957
|
-
const statusTimer = (0,
|
|
1958
|
-
const showStatus = (0,
|
|
1677
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react9.useState)(0);
|
|
1678
|
+
const [status, setStatusMsg] = (0, import_react9.useState)("");
|
|
1679
|
+
const statusTimer = (0, import_react9.useRef)(null);
|
|
1680
|
+
const showStatus = (0, import_react9.useCallback)((msg) => {
|
|
1959
1681
|
setStatusMsg(msg);
|
|
1960
1682
|
if (statusTimer.current) clearTimeout(statusTimer.current);
|
|
1961
1683
|
statusTimer.current = setTimeout(() => setStatusMsg(""), 2500);
|
|
@@ -1965,7 +1687,7 @@ function BossTasksOverlay({
|
|
|
1965
1687
|
tasks: tasks.filter((t) => t.project === w.name).sort((a, b) => a.createdAt.localeCompare(b.createdAt))
|
|
1966
1688
|
}));
|
|
1967
1689
|
const flatTasks = groupedTasks.flatMap((g) => g.tasks);
|
|
1968
|
-
(0,
|
|
1690
|
+
(0, import_react9.useEffect)(() => {
|
|
1969
1691
|
if (flatTasks.length === 0) {
|
|
1970
1692
|
setSelectedIndex(0);
|
|
1971
1693
|
} else if (selectedIndex >= flatTasks.length) {
|
|
@@ -2025,11 +1747,11 @@ function BossTasksOverlay({
|
|
|
2025
1747
|
const inProgressCount = tasks.filter((t) => t.status === "in_progress").length;
|
|
2026
1748
|
const pendingCount = tasks.filter((t) => t.status === "pending").length;
|
|
2027
1749
|
const blockedCount = tasks.filter((t) => t.status === "blocked").length;
|
|
2028
|
-
return /* @__PURE__ */ (0,
|
|
2029
|
-
/* @__PURE__ */ (0,
|
|
2030
|
-
/* @__PURE__ */ (0,
|
|
2031
|
-
/* @__PURE__ */ (0,
|
|
2032
|
-
/* @__PURE__ */ (0,
|
|
1750
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, paddingX: 1, children: [
|
|
1751
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
|
|
1752
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: COLORS.primary, bold: true, children: "Tasks" }),
|
|
1753
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${tasks.length} total \xB7 ` }),
|
|
1754
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2033
1755
|
CountsRow,
|
|
2034
1756
|
{
|
|
2035
1757
|
theme,
|
|
@@ -2040,28 +1762,28 @@ function BossTasksOverlay({
|
|
|
2040
1762
|
}
|
|
2041
1763
|
)
|
|
2042
1764
|
] }),
|
|
2043
|
-
flatTasks.length === 0 && /* @__PURE__ */ (0,
|
|
1765
|
+
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: [
|
|
2044
1766
|
" No tasks yet. Ask the boss to plan some \u2014 e.g. ",
|
|
2045
|
-
/* @__PURE__ */ (0,
|
|
1767
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.text, children: '"plan some work"' }),
|
|
2046
1768
|
"."
|
|
2047
1769
|
] }) }),
|
|
2048
|
-
showingTop && /* @__PURE__ */ (0,
|
|
1770
|
+
showingTop && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: ` \u2191 ${startIdx} more above` }),
|
|
2049
1771
|
groupedTasks.map((group, gIdx) => {
|
|
2050
1772
|
const startInFlat = groupedTasks.slice(0, gIdx).reduce((acc, g) => acc + g.tasks.length, 0);
|
|
2051
1773
|
const visibleInSection = group.tasks.filter((t) => visibleIdSet.has(t.id));
|
|
2052
1774
|
if (visibleInSection.length === 0) return null;
|
|
2053
|
-
return /* @__PURE__ */ (0,
|
|
2054
|
-
/* @__PURE__ */ (0,
|
|
2055
|
-
/* @__PURE__ */ (0,
|
|
2056
|
-
/* @__PURE__ */ (0,
|
|
1775
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
|
|
1776
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
|
|
1777
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: projectColor(group.project), bold: true, children: group.project }),
|
|
1778
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${group.tasks.length}` })
|
|
2057
1779
|
] }),
|
|
2058
1780
|
visibleInSection.map((task) => {
|
|
2059
1781
|
const realIdx = startInFlat + group.tasks.indexOf(task);
|
|
2060
1782
|
const isSelected = realIdx === selectedIndex;
|
|
2061
1783
|
const prefix = isSelected ? "\u276F " : " ";
|
|
2062
1784
|
const glyph = statusGlyph(task.status);
|
|
2063
|
-
const
|
|
2064
|
-
return /* @__PURE__ */ (0,
|
|
1785
|
+
const color2 = isSelected ? theme.primary : task.status === "done" ? theme.success : task.status === "in_progress" ? theme.warning : task.status === "blocked" ? theme.error : theme.text;
|
|
1786
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: color2, bold: isSelected, children: [
|
|
2065
1787
|
prefix,
|
|
2066
1788
|
"[",
|
|
2067
1789
|
glyph,
|
|
@@ -2071,16 +1793,16 @@ function BossTasksOverlay({
|
|
|
2071
1793
|
})
|
|
2072
1794
|
] }, group.project);
|
|
2073
1795
|
}),
|
|
2074
|
-
showingBottom && /* @__PURE__ */ (0,
|
|
2075
|
-
status && /* @__PURE__ */ (0,
|
|
2076
|
-
/* @__PURE__ */ (0,
|
|
2077
|
-
/* @__PURE__ */ (0,
|
|
1796
|
+
showingBottom && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: ` \u2193 ${flatTasks.length - endIdx} more below` }),
|
|
1797
|
+
status && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.success, children: " " + status }) }),
|
|
1798
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.textDim, children: [
|
|
1799
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.primary, children: "\u2191\u2193" }),
|
|
2078
1800
|
" move \xB7 (",
|
|
2079
|
-
/* @__PURE__ */ (0,
|
|
1801
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.primary, children: "d" }),
|
|
2080
1802
|
")elete \xB7 (",
|
|
2081
|
-
/* @__PURE__ */ (0,
|
|
1803
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.primary, children: "r" }),
|
|
2082
1804
|
")un pending \xB7 ",
|
|
2083
|
-
/* @__PURE__ */ (0,
|
|
1805
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.primary, children: "ESC" }),
|
|
2084
1806
|
" close"
|
|
2085
1807
|
] }) })
|
|
2086
1808
|
] });
|
|
@@ -2092,24 +1814,24 @@ function CountsRow({
|
|
|
2092
1814
|
pending,
|
|
2093
1815
|
blocked
|
|
2094
1816
|
}) {
|
|
2095
|
-
return /* @__PURE__ */ (0,
|
|
2096
|
-
/* @__PURE__ */ (0,
|
|
1817
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
|
|
1818
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.success, children: [
|
|
2097
1819
|
done,
|
|
2098
1820
|
" done"
|
|
2099
1821
|
] }),
|
|
2100
|
-
/* @__PURE__ */ (0,
|
|
2101
|
-
/* @__PURE__ */ (0,
|
|
1822
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
|
|
1823
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.warning, children: [
|
|
2102
1824
|
active,
|
|
2103
1825
|
" active"
|
|
2104
1826
|
] }),
|
|
2105
|
-
/* @__PURE__ */ (0,
|
|
2106
|
-
/* @__PURE__ */ (0,
|
|
1827
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
|
|
1828
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.text, children: [
|
|
2107
1829
|
pending,
|
|
2108
1830
|
" pending"
|
|
2109
1831
|
] }),
|
|
2110
|
-
blocked > 0 && /* @__PURE__ */ (0,
|
|
2111
|
-
/* @__PURE__ */ (0,
|
|
2112
|
-
/* @__PURE__ */ (0,
|
|
1832
|
+
blocked > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
1833
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
|
|
1834
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme.error, children: [
|
|
2113
1835
|
blocked,
|
|
2114
1836
|
" blocked"
|
|
2115
1837
|
] })
|
|
@@ -2117,9 +1839,100 @@ function CountsRow({
|
|
|
2117
1839
|
] });
|
|
2118
1840
|
}
|
|
2119
1841
|
|
|
1842
|
+
// src/boss-worker-status-row.tsx
|
|
1843
|
+
init_esm_shims();
|
|
1844
|
+
var import_react10 = __toESM(require_react(), 1);
|
|
1845
|
+
var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
|
|
1846
|
+
var SHIMMER_WIDTH = 3;
|
|
1847
|
+
function formatWorkerElapsed(ms) {
|
|
1848
|
+
const total = Math.floor(ms / 1e3);
|
|
1849
|
+
const m = Math.floor(total / 60);
|
|
1850
|
+
const s = total % 60;
|
|
1851
|
+
return `${m}:${s.toString().padStart(2, "0")}`;
|
|
1852
|
+
}
|
|
1853
|
+
function AnimationActiveSentinel() {
|
|
1854
|
+
useAnimationActive();
|
|
1855
|
+
return null;
|
|
1856
|
+
}
|
|
1857
|
+
function ShimmerName({
|
|
1858
|
+
name,
|
|
1859
|
+
color: color2,
|
|
1860
|
+
tick
|
|
1861
|
+
}) {
|
|
1862
|
+
const cycle = name.length + SHIMMER_WIDTH * 2;
|
|
1863
|
+
const shimmerPos = tick % cycle - SHIMMER_WIDTH;
|
|
1864
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: name.split("").map((ch, i) => {
|
|
1865
|
+
const isBright = Math.abs(i - shimmerPos) <= SHIMMER_WIDTH;
|
|
1866
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: color2, bold: isBright, dimColor: !isBright, children: ch }, i);
|
|
1867
|
+
}) });
|
|
1868
|
+
}
|
|
1869
|
+
function BossWorkerStatusRow({
|
|
1870
|
+
workers,
|
|
1871
|
+
pendingMessages
|
|
1872
|
+
}) {
|
|
1873
|
+
const theme = useTheme();
|
|
1874
|
+
const { columns } = useTerminalSize();
|
|
1875
|
+
const working = workers.filter((w) => w.status === "working");
|
|
1876
|
+
const errored = workers.filter((w) => w.status === "error");
|
|
1877
|
+
const idleCount = workers.length - working.length - errored.length;
|
|
1878
|
+
const anyWorking = working.length > 0;
|
|
1879
|
+
const tick = useAnimationTick();
|
|
1880
|
+
const now = Date.now();
|
|
1881
|
+
if (workers.length === 0) return null;
|
|
1882
|
+
const slots = [];
|
|
1883
|
+
for (const w of working) {
|
|
1884
|
+
const projectHue = projectColor(w.name);
|
|
1885
|
+
const elapsed = w.workStartedAt ? formatWorkerElapsed(now - w.workStartedAt) : null;
|
|
1886
|
+
slots.push(
|
|
1887
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react10.default.Fragment, { children: [
|
|
1888
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ShimmerName, { name: w.name, color: projectHue, tick }),
|
|
1889
|
+
elapsed && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.textDim, children: [
|
|
1890
|
+
" ",
|
|
1891
|
+
elapsed
|
|
1892
|
+
] })
|
|
1893
|
+
] }, `w-${w.name}`)
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
for (const w of errored) {
|
|
1897
|
+
slots.push(
|
|
1898
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react10.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.error, children: [
|
|
1899
|
+
"\u2717 ",
|
|
1900
|
+
w.name
|
|
1901
|
+
] }) }, `e-${w.name}`)
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1904
|
+
if (idleCount > 0) {
|
|
1905
|
+
slots.push(
|
|
1906
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react10.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.textDim, children: [
|
|
1907
|
+
"\u25CB ",
|
|
1908
|
+
idleCount,
|
|
1909
|
+
" idle"
|
|
1910
|
+
] }) }, "idle")
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { paddingX: 1, width: columns, flexShrink: 1, children: [
|
|
1914
|
+
anyWorking && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AnimationActiveSentinel, {}),
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "truncate", children: [
|
|
1916
|
+
slots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react10.default.Fragment, { children: [
|
|
1917
|
+
i > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.border, children: " \u2502 " }),
|
|
1918
|
+
slot
|
|
1919
|
+
] }, i)),
|
|
1920
|
+
pendingMessages > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
1921
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " " }),
|
|
1922
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.warning, children: [
|
|
1923
|
+
pendingMessages,
|
|
1924
|
+
" message",
|
|
1925
|
+
pendingMessages === 1 ? "" : "s",
|
|
1926
|
+
" queued"
|
|
1927
|
+
] })
|
|
1928
|
+
] })
|
|
1929
|
+
] })
|
|
1930
|
+
] });
|
|
1931
|
+
}
|
|
1932
|
+
|
|
2120
1933
|
// src/radio-picker.tsx
|
|
2121
1934
|
init_esm_shims();
|
|
2122
|
-
var
|
|
1935
|
+
var import_react11 = __toESM(require_react(), 1);
|
|
2123
1936
|
|
|
2124
1937
|
// src/radio.ts
|
|
2125
1938
|
init_esm_shims();
|
|
@@ -2287,7 +2100,7 @@ function buildInstallHint() {
|
|
|
2287
2100
|
}
|
|
2288
2101
|
|
|
2289
2102
|
// src/radio-picker.tsx
|
|
2290
|
-
var
|
|
2103
|
+
var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
|
|
2291
2104
|
function RadioPicker({
|
|
2292
2105
|
currentStationId: currentStationId2,
|
|
2293
2106
|
onSelect,
|
|
@@ -2309,7 +2122,7 @@ function RadioPicker({
|
|
|
2309
2122
|
0,
|
|
2310
2123
|
items.findIndex((i) => i.value === (currentStationId2 ?? "off"))
|
|
2311
2124
|
);
|
|
2312
|
-
return /* @__PURE__ */ (0,
|
|
2125
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2313
2126
|
SelectList,
|
|
2314
2127
|
{
|
|
2315
2128
|
items,
|
|
@@ -2321,197 +2134,908 @@ function RadioPicker({
|
|
|
2321
2134
|
);
|
|
2322
2135
|
}
|
|
2323
2136
|
|
|
2324
|
-
// src/
|
|
2137
|
+
// src/boss-chat-screen.tsx
|
|
2138
|
+
var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
|
|
2139
|
+
function BossChatScreen({
|
|
2140
|
+
boss,
|
|
2141
|
+
columns,
|
|
2142
|
+
state,
|
|
2143
|
+
overlay,
|
|
2144
|
+
controlsRef = () => {
|
|
2145
|
+
},
|
|
2146
|
+
livePane,
|
|
2147
|
+
theme,
|
|
2148
|
+
statusSlotVisible,
|
|
2149
|
+
activityVisible,
|
|
2150
|
+
stallStatusVisible,
|
|
2151
|
+
doneStatus,
|
|
2152
|
+
elapsedMs,
|
|
2153
|
+
runStartRef,
|
|
2154
|
+
charCountRef,
|
|
2155
|
+
realTokensAccumRef,
|
|
2156
|
+
lastUserMessage,
|
|
2157
|
+
activeToolNames,
|
|
2158
|
+
inputActive,
|
|
2159
|
+
isRunning,
|
|
2160
|
+
onSubmit,
|
|
2161
|
+
onAbort,
|
|
2162
|
+
onTab,
|
|
2163
|
+
onShiftTab,
|
|
2164
|
+
commands,
|
|
2165
|
+
scopeBadge,
|
|
2166
|
+
onCloseOverlay,
|
|
2167
|
+
onModelSelect,
|
|
2168
|
+
currentRadio,
|
|
2169
|
+
onRadioSelect,
|
|
2170
|
+
bossModel,
|
|
2171
|
+
workerModel,
|
|
2172
|
+
updatePending,
|
|
2173
|
+
currentRadioStationId,
|
|
2174
|
+
workers,
|
|
2175
|
+
pendingMessages,
|
|
2176
|
+
formatDuration
|
|
2177
|
+
}) {
|
|
2178
|
+
if (overlay === "tasks") {
|
|
2179
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChatLayout, { columns, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(BossTasksOverlay, { boss, workers, onClose: onCloseOverlay }) });
|
|
2180
|
+
}
|
|
2181
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(ChatLayout, { columns, children: [
|
|
2182
|
+
livePane,
|
|
2183
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(ChatControls, { controlsRef, children: [
|
|
2184
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2185
|
+
ChatInputStack,
|
|
2186
|
+
{
|
|
2187
|
+
columns,
|
|
2188
|
+
theme,
|
|
2189
|
+
statusSlotVisible,
|
|
2190
|
+
activityVisible,
|
|
2191
|
+
stallStatusVisible,
|
|
2192
|
+
liveToolFeed: [],
|
|
2193
|
+
doneStatus,
|
|
2194
|
+
activityPhase: state.activityPhase,
|
|
2195
|
+
elapsedMs,
|
|
2196
|
+
runStartRef,
|
|
2197
|
+
thinkingMs: state.streaming?.thinkingMs ?? 0,
|
|
2198
|
+
isThinking: state.activityPhase === "thinking",
|
|
2199
|
+
thinkingLevel: state.bossThinkingLevel,
|
|
2200
|
+
tokenEstimate: state.bossInputTokens,
|
|
2201
|
+
charCountRef,
|
|
2202
|
+
realTokensAccumRef,
|
|
2203
|
+
lastUserMessage,
|
|
2204
|
+
activeToolNames,
|
|
2205
|
+
retryInfo: state.retryInfo,
|
|
2206
|
+
planDone: 0,
|
|
2207
|
+
planTotal: 0,
|
|
2208
|
+
renderMarkdown: true,
|
|
2209
|
+
formatDuration
|
|
2210
|
+
}
|
|
2211
|
+
),
|
|
2212
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2213
|
+
InputArea,
|
|
2214
|
+
{
|
|
2215
|
+
onSubmit,
|
|
2216
|
+
onAbort,
|
|
2217
|
+
disabled: isRunning,
|
|
2218
|
+
isActive: inputActive,
|
|
2219
|
+
cwd: process.cwd(),
|
|
2220
|
+
commands,
|
|
2221
|
+
scopeBadge,
|
|
2222
|
+
disableMouseTracking: true,
|
|
2223
|
+
onTab,
|
|
2224
|
+
onShiftTab
|
|
2225
|
+
}
|
|
2226
|
+
),
|
|
2227
|
+
overlay === "model-boss" || overlay === "model-workers" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2228
|
+
BossModelSelector,
|
|
2229
|
+
{
|
|
2230
|
+
onSelect: onModelSelect,
|
|
2231
|
+
onCancel: onCloseOverlay,
|
|
2232
|
+
currentModel: overlay === "model-boss" ? state.bossModel : state.workerModel,
|
|
2233
|
+
currentProvider: overlay === "model-boss" ? state.bossProvider : state.workerProvider
|
|
2234
|
+
}
|
|
2235
|
+
) : overlay === "radio" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2236
|
+
RadioPicker,
|
|
2237
|
+
{
|
|
2238
|
+
currentStationId: currentRadio,
|
|
2239
|
+
onCancel: onCloseOverlay,
|
|
2240
|
+
onSelect: onRadioSelect
|
|
2241
|
+
}
|
|
2242
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
2243
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2244
|
+
BossFooter,
|
|
2245
|
+
{
|
|
2246
|
+
bossModel,
|
|
2247
|
+
workerModel,
|
|
2248
|
+
tokensIn: state.bossInputTokens,
|
|
2249
|
+
exitPending: state.exitPending,
|
|
2250
|
+
bossThinkingLevel: state.bossThinkingLevel,
|
|
2251
|
+
updatePending,
|
|
2252
|
+
currentRadioStationId,
|
|
2253
|
+
scope: state.scope
|
|
2254
|
+
}
|
|
2255
|
+
),
|
|
2256
|
+
!state.exitPending && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(BossWorkerStatusRow, { workers, pendingMessages })
|
|
2257
|
+
] })
|
|
2258
|
+
] })
|
|
2259
|
+
] });
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// src/slash-commands.ts
|
|
2325
2263
|
init_esm_shims();
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
} catch {
|
|
2342
|
-
return null;
|
|
2343
|
-
}
|
|
2264
|
+
var BOSS_SLASH_COMMANDS = [
|
|
2265
|
+
{ name: "help", aliases: ["?"], description: "Show available commands" },
|
|
2266
|
+
{
|
|
2267
|
+
name: "model-boss",
|
|
2268
|
+
aliases: ["m", "model", "models"],
|
|
2269
|
+
description: "Switch the orchestrator's model"
|
|
2270
|
+
},
|
|
2271
|
+
{ name: "model-workers", aliases: [], description: "Switch every worker's model" },
|
|
2272
|
+
{ name: "compact", aliases: [], description: "Compact the boss's context now" },
|
|
2273
|
+
{ name: "clear", aliases: [], description: "Clear chat history and terminal" },
|
|
2274
|
+
{ name: "radio", aliases: [], description: "Stream a free internet radio station" },
|
|
2275
|
+
{ name: "quit", aliases: ["q", "exit"], description: "Exit gg-boss" }
|
|
2276
|
+
];
|
|
2277
|
+
function isSlashCommand(value) {
|
|
2278
|
+
return value.startsWith("/") && !value.startsWith("//");
|
|
2344
2279
|
}
|
|
2345
|
-
function
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
}
|
|
2280
|
+
function parseSlash(value) {
|
|
2281
|
+
if (!isSlashCommand(value)) return null;
|
|
2282
|
+
const rest = value.slice(1).trim();
|
|
2283
|
+
if (!rest) return null;
|
|
2284
|
+
const space = rest.indexOf(" ");
|
|
2285
|
+
if (space === -1) return { name: rest.toLowerCase(), args: "" };
|
|
2286
|
+
return { name: rest.slice(0, space).toLowerCase(), args: rest.slice(space + 1).trim() };
|
|
2352
2287
|
}
|
|
2353
|
-
function
|
|
2354
|
-
const
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
2358
|
-
if (diff !== 0) return diff;
|
|
2288
|
+
function canonicalName(name) {
|
|
2289
|
+
for (const cmd of BOSS_SLASH_COMMANDS) {
|
|
2290
|
+
if (cmd.name === name) return cmd.name;
|
|
2291
|
+
if (cmd.aliases.includes(name)) return cmd.name;
|
|
2359
2292
|
}
|
|
2360
|
-
return
|
|
2293
|
+
return null;
|
|
2361
2294
|
}
|
|
2362
|
-
function
|
|
2363
|
-
const
|
|
2364
|
-
|
|
2365
|
-
|
|
2295
|
+
function buildHelpText() {
|
|
2296
|
+
const lines = ["**gg-boss commands**", ""];
|
|
2297
|
+
for (const cmd of BOSS_SLASH_COMMANDS) {
|
|
2298
|
+
const aliases = cmd.aliases.length > 0 ? ` (${cmd.aliases.map((a) => "/" + a).join(", ")})` : "";
|
|
2299
|
+
lines.push(`- \`/${cmd.name}\`${aliases} \u2014 ${cmd.description}`);
|
|
2366
2300
|
}
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2301
|
+
lines.push("");
|
|
2302
|
+
lines.push("**Global keybindings**");
|
|
2303
|
+
lines.push("- `Ctrl+T` \u2014 open the Tasks pane");
|
|
2304
|
+
lines.push("- `Tab` \u2014 switch project scope (All / per-project pill in the input)");
|
|
2305
|
+
lines.push("- `Shift+Tab` \u2014 cycle the boss's thinking level, then off");
|
|
2306
|
+
lines.push("- `Esc` \u2014 interrupt the boss while it's running");
|
|
2307
|
+
lines.push("- `Ctrl+C` (twice) \u2014 exit");
|
|
2308
|
+
lines.push("");
|
|
2309
|
+
lines.push("**Inside the Tasks pane (Ctrl+T)**");
|
|
2310
|
+
lines.push("- `\u2191` / `\u2193` (or `k` / `j`) \u2014 navigate tasks");
|
|
2311
|
+
lines.push("- `r` \u2014 run all pending and blocked tasks across idle workers");
|
|
2312
|
+
lines.push("- `d` \u2014 delete the selected task");
|
|
2313
|
+
lines.push("- `Esc` \u2014 close the Tasks pane");
|
|
2314
|
+
lines.push("");
|
|
2315
|
+
lines.push("**Inside model pickers (`/model`, `/models`, `/model-boss`, `/model-workers`)**");
|
|
2316
|
+
lines.push("- `\u2191` / `\u2193` \u2014 navigate models");
|
|
2317
|
+
lines.push("- `Enter` \u2014 select");
|
|
2318
|
+
lines.push("- `Esc` \u2014 cancel");
|
|
2319
|
+
lines.push("");
|
|
2320
|
+
lines.push("**Radio** (`/radio`)");
|
|
2321
|
+
lines.push("- Pick a station to stream while you work, or select `Off` to stop.");
|
|
2322
|
+
lines.push("- Requires `mpv` (recommended), `ffplay`, `mpg123`, or `vlc/cvlc` installed.");
|
|
2323
|
+
lines.push("");
|
|
2324
|
+
lines.push("**Input area**");
|
|
2325
|
+
lines.push("- `\u2191` / `\u2193` \u2014 recall previous prompts (when input is empty)");
|
|
2326
|
+
lines.push("- `Enter` \u2014 send \xB7 `Shift+Enter` \u2014 newline");
|
|
2327
|
+
lines.push("- `/` \u2014 open the slash-command menu (Tab / arrows to pick, Enter to insert)");
|
|
2328
|
+
return lines.join("\n");
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// src/boss-transcript-rows.tsx
|
|
2332
|
+
init_esm_shims();
|
|
2333
|
+
var import_react13 = __toESM(require_react(), 1);
|
|
2334
|
+
|
|
2335
|
+
// src/boss-spacing.ts
|
|
2336
|
+
init_esm_shims();
|
|
2337
|
+
var BOSS_SPACING_KINDS = /* @__PURE__ */ new Set([
|
|
2338
|
+
"user",
|
|
2339
|
+
"assistant",
|
|
2340
|
+
"tool_start",
|
|
2341
|
+
"tool_done",
|
|
2342
|
+
"tool_group",
|
|
2343
|
+
"worker_event",
|
|
2344
|
+
"worker_error",
|
|
2345
|
+
"task_dispatch",
|
|
2346
|
+
"info",
|
|
2347
|
+
"update_notice",
|
|
2348
|
+
"compacting",
|
|
2349
|
+
"compacted",
|
|
2350
|
+
"stopped"
|
|
2351
|
+
]);
|
|
2352
|
+
var BOSS_COMPACT_BOUNDARIES = /* @__PURE__ */ new Set([
|
|
2353
|
+
"user\u2192assistant",
|
|
2354
|
+
"assistant\u2192user",
|
|
2355
|
+
"user\u2192queued"
|
|
2356
|
+
]);
|
|
2357
|
+
|
|
2358
|
+
// src/tool-formatters.ts
|
|
2359
|
+
init_esm_shims();
|
|
2360
|
+
function truncate2(s, max) {
|
|
2361
|
+
if (max <= 1) return "\u2026";
|
|
2362
|
+
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
2363
|
+
}
|
|
2364
|
+
function promptWorkerDetailLen(project) {
|
|
2365
|
+
const cols = process.stdout.columns ?? 80;
|
|
2366
|
+
const fixed = 2 + 13 + 1 + project.length + 3 + 1 + 1 + 11 + 6;
|
|
2367
|
+
return Math.max(20, cols - fixed);
|
|
2368
|
+
}
|
|
2369
|
+
var bossToolFormatters = {
|
|
2370
|
+
formatLabel(name) {
|
|
2371
|
+
switch (name) {
|
|
2372
|
+
case "list_workers":
|
|
2373
|
+
return "List Workers";
|
|
2374
|
+
case "get_worker_status":
|
|
2375
|
+
return "Worker Status";
|
|
2376
|
+
case "prompt_worker":
|
|
2377
|
+
return "Prompt Worker";
|
|
2378
|
+
case "get_worker_summary":
|
|
2379
|
+
return "Worker Summary";
|
|
2380
|
+
default:
|
|
2381
|
+
return void 0;
|
|
2382
|
+
}
|
|
2383
|
+
},
|
|
2384
|
+
formatDetail(name, args) {
|
|
2385
|
+
switch (name) {
|
|
2386
|
+
case "list_workers":
|
|
2387
|
+
return "";
|
|
2388
|
+
case "get_worker_status":
|
|
2389
|
+
case "get_worker_summary":
|
|
2390
|
+
return truncate2(String(args.project ?? ""), 40);
|
|
2391
|
+
case "prompt_worker": {
|
|
2392
|
+
const project = String(args.project ?? "");
|
|
2393
|
+
const message = String(args.message ?? "").replace(/\s+/g, " ");
|
|
2394
|
+
const fresh = args.fresh === true;
|
|
2395
|
+
const maxMsg = promptWorkerDetailLen(project) - (fresh ? 8 : 0);
|
|
2396
|
+
const truncMsg = truncate2(message, Math.max(15, maxMsg));
|
|
2397
|
+
const head = fresh ? "fresh \xB7 " : "";
|
|
2398
|
+
return project ? `${head}${project} \xB7 ${truncMsg}` : `${head}${truncMsg}`;
|
|
2399
|
+
}
|
|
2400
|
+
default:
|
|
2401
|
+
return void 0;
|
|
2402
|
+
}
|
|
2403
|
+
},
|
|
2404
|
+
formatInline(name, result, isError) {
|
|
2405
|
+
if (isError) return void 0;
|
|
2406
|
+
switch (name) {
|
|
2407
|
+
case "list_workers": {
|
|
2408
|
+
const lines = result.split("\n").filter((l) => l.startsWith("-"));
|
|
2409
|
+
return `${lines.length} worker${lines.length === 1 ? "" : "s"}`;
|
|
2410
|
+
}
|
|
2411
|
+
case "prompt_worker": {
|
|
2412
|
+
if (result.includes("currently working")) {
|
|
2413
|
+
return { text: "busy \u2014 skipped", color: "#fbbf24" };
|
|
2414
|
+
}
|
|
2415
|
+
if (result.includes("Unknown project")) {
|
|
2416
|
+
return { text: "unknown project", color: "#f87171" };
|
|
2417
|
+
}
|
|
2418
|
+
const project = String(result.match(/"([^"]+)"/)?.[1] ?? "");
|
|
2419
|
+
const color2 = project ? projectColor(project) : "#e11d48";
|
|
2420
|
+
return { text: "dispatched", color: color2 };
|
|
2421
|
+
}
|
|
2422
|
+
case "get_worker_status": {
|
|
2423
|
+
const parts = result.split(":");
|
|
2424
|
+
if (parts.length < 2) return void 0;
|
|
2425
|
+
const status = parts.slice(1).join(":").trim();
|
|
2426
|
+
const project = parts[0].trim();
|
|
2427
|
+
return { text: status, color: projectColor(project) };
|
|
2428
|
+
}
|
|
2429
|
+
case "get_worker_summary": {
|
|
2430
|
+
const turnMatch = result.match(/Turn:\s*(\d+)/);
|
|
2431
|
+
const projectMatch = result.match(/Project:\s*(.+)/);
|
|
2432
|
+
const toolsMatch = result.match(/Tools used:\s*(.+)/);
|
|
2433
|
+
const tools = toolsMatch ? toolsMatch[1] : "";
|
|
2434
|
+
const toolCount = tools && tools !== "(no tools used)" ? tools.split(",").length : 0;
|
|
2435
|
+
const turn = turnMatch ? `turn ${turnMatch[1]}` : void 0;
|
|
2436
|
+
const tCount = toolCount > 0 ? `${toolCount} tool${toolCount === 1 ? "" : "s"}` : void 0;
|
|
2437
|
+
const summary = [turn, tCount].filter(Boolean).join(" \xB7 ");
|
|
2438
|
+
if (!summary) return void 0;
|
|
2439
|
+
const project = projectMatch ? projectMatch[1].trim() : "";
|
|
2440
|
+
return project ? { text: summary, color: projectColor(project) } : { text: summary, color: "#9ca3af" };
|
|
2441
|
+
}
|
|
2442
|
+
default:
|
|
2443
|
+
return void 0;
|
|
2444
|
+
}
|
|
2372
2445
|
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2446
|
+
};
|
|
2447
|
+
|
|
2448
|
+
// src/boss-transcript-rows.tsx
|
|
2449
|
+
var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
|
|
2450
|
+
function getBossTranscriptMarginTop({
|
|
2451
|
+
row,
|
|
2452
|
+
previousRow
|
|
2453
|
+
}) {
|
|
2454
|
+
if (row.kind === "banner" || previousRow?.kind === "banner") return 0;
|
|
2455
|
+
if (!BOSS_SPACING_KINDS.has(row.kind)) return 0;
|
|
2456
|
+
return getTranscriptItemMarginTop({
|
|
2457
|
+
item: row,
|
|
2458
|
+
previousLiveItem: previousRow,
|
|
2459
|
+
spacingKinds: BOSS_SPACING_KINDS,
|
|
2460
|
+
compactBoundaries: BOSS_COMPACT_BOUNDARIES
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
function BossTranscriptRow({
|
|
2464
|
+
row,
|
|
2465
|
+
previousRow
|
|
2466
|
+
}) {
|
|
2467
|
+
if (row.kind === "banner") {
|
|
2468
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BossBanner, { subtitle: "Orchestrator", showShortcuts: true }) });
|
|
2469
|
+
}
|
|
2470
|
+
const marginTop = getBossTranscriptMarginTop({ row, previousRow });
|
|
2471
|
+
const renderWithSpacing = (node) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TranscriptItemFrame, { marginTop, children: node });
|
|
2472
|
+
if (row.kind === "user") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UserMessage, { text: row.text }));
|
|
2473
|
+
if (row.kind === "assistant") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AssistantRow, { item: row }));
|
|
2474
|
+
if (row.kind === "tool_start") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToolStartHistoryRow, { item: row }));
|
|
2475
|
+
if (row.kind === "tool_done") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToolHistoryRow, { item: row }));
|
|
2476
|
+
if (row.kind === "tool_group") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToolGroupRow, { item: row }));
|
|
2477
|
+
if (row.kind === "worker_event") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(WorkerEventRow, { item: row }));
|
|
2478
|
+
if (row.kind === "worker_error") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(WorkerErrorRow, { item: row }));
|
|
2479
|
+
if (row.kind === "info") {
|
|
2480
|
+
return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InfoRow, { text: row.text, level: row.level ?? "info" }));
|
|
2481
|
+
}
|
|
2482
|
+
if (row.kind === "task_dispatch") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TaskDispatchRow, { tasks: row.tasks }));
|
|
2483
|
+
if (row.kind === "update_notice") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpdateNoticeRow, { text: row.text }));
|
|
2484
|
+
if (row.kind === "compacting") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CompactionSpinner, { staticDisplay: true }));
|
|
2485
|
+
if (row.kind === "compacted") {
|
|
2486
|
+
return renderWithSpacing(
|
|
2487
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2488
|
+
CompactionDone,
|
|
2489
|
+
{
|
|
2490
|
+
originalCount: row.originalCount,
|
|
2491
|
+
newCount: row.newCount,
|
|
2492
|
+
tokensBefore: row.tokensBefore,
|
|
2493
|
+
tokensAfter: row.tokensAfter
|
|
2494
|
+
}
|
|
2495
|
+
)
|
|
2496
|
+
);
|
|
2378
2497
|
}
|
|
2379
|
-
return {
|
|
2380
|
-
|
|
2381
|
-
updateCommand: `npm install -g ${PACKAGE_NAME}@latest`
|
|
2382
|
-
};
|
|
2498
|
+
if (row.kind === "stopped") return renderWithSpacing(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InfoRow, { text: row.text, level: "warning" }));
|
|
2499
|
+
return null;
|
|
2383
2500
|
}
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2501
|
+
function UpdateNoticeRow({ text }) {
|
|
2502
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexShrink: 1, borderStyle: "round", borderColor: COLORS.accent, paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { wrap: "wrap", children: [
|
|
2503
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: COLORS.accent, bold: true, children: "\u2728 " }),
|
|
2504
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: COLORS.primary, bold: true, children: text })
|
|
2505
|
+
] }) });
|
|
2506
|
+
}
|
|
2507
|
+
function TaskDispatchRow({
|
|
2508
|
+
tasks
|
|
2509
|
+
}) {
|
|
2510
|
+
const theme = useTheme();
|
|
2511
|
+
const count = tasks.length;
|
|
2512
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
|
|
2513
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { children: [
|
|
2514
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: COLORS.primary, bold: true, children: "\u23FA " }),
|
|
2515
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { color: theme.text, bold: true, children: [
|
|
2516
|
+
"Running ",
|
|
2517
|
+
count,
|
|
2518
|
+
" task",
|
|
2519
|
+
count === 1 ? "" : "s",
|
|
2520
|
+
":"
|
|
2521
|
+
] })
|
|
2522
|
+
] }),
|
|
2523
|
+
tasks.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { children: [
|
|
2524
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: theme.textDim, children: " \u2022 " }),
|
|
2525
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: projectColor(t.project), bold: true, children: t.project }),
|
|
2526
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: theme.textDim, children: ": " }),
|
|
2527
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: theme.text, children: t.title })
|
|
2528
|
+
] }, `${t.project}-${i}`))
|
|
2529
|
+
] });
|
|
2530
|
+
}
|
|
2531
|
+
var SHORTCUT_PATTERNS = [
|
|
2532
|
+
// Modifier+Key combos: Ctrl+T, Shift+Tab, Cmd+K, Ctrl+Shift+P, Ctrl+C
|
|
2533
|
+
/\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,
|
|
2534
|
+
// Bare named keys (only when surrounded by clear key context)
|
|
2535
|
+
/\b(?:Ctrl-[A-Z]|F[1-9]|F1[0-2])\b/g
|
|
2536
|
+
];
|
|
2537
|
+
function highlightShortcuts(text) {
|
|
2538
|
+
if (!text) return text;
|
|
2539
|
+
const SENTINEL = "\uE000";
|
|
2540
|
+
const masks = [];
|
|
2541
|
+
let masked = text.replace(/```[\s\S]*?```|`[^`]+`/g, (m) => {
|
|
2542
|
+
const idx = masks.push(m) - 1;
|
|
2543
|
+
return `${SENTINEL}${idx}${SENTINEL}`;
|
|
2544
|
+
});
|
|
2545
|
+
for (const re of SHORTCUT_PATTERNS) {
|
|
2546
|
+
masked = masked.replace(re, (m) => `\`${m}\``);
|
|
2395
2547
|
}
|
|
2548
|
+
return masked.replace(
|
|
2549
|
+
new RegExp(`${SENTINEL}(\\d+)${SENTINEL}`, "g"),
|
|
2550
|
+
(_, i) => masks[Number(i)]
|
|
2551
|
+
);
|
|
2396
2552
|
}
|
|
2397
|
-
function
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2553
|
+
function AssistantRow({ item }) {
|
|
2554
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AssistantMessage, { text: highlightShortcuts(item.text), renderMarkdown: true });
|
|
2555
|
+
}
|
|
2556
|
+
function ToolStartHistoryRow({
|
|
2557
|
+
item
|
|
2558
|
+
}) {
|
|
2559
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2560
|
+
ToolExecution,
|
|
2561
|
+
{
|
|
2562
|
+
status: "running",
|
|
2563
|
+
name: item.name,
|
|
2564
|
+
args: item.args,
|
|
2565
|
+
formatters: bossToolFormatters
|
|
2566
|
+
}
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
function ToolGroupRow({ item }) {
|
|
2570
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToolGroupExecution, { tools: item.tools, summaryRenderers: bossToolGroupRenderers });
|
|
2571
|
+
}
|
|
2572
|
+
function ToolHistoryRow({ item }) {
|
|
2573
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2574
|
+
ToolExecution,
|
|
2575
|
+
{
|
|
2576
|
+
status: "done",
|
|
2577
|
+
name: item.name,
|
|
2578
|
+
args: item.args,
|
|
2579
|
+
result: item.result,
|
|
2580
|
+
isError: item.isError,
|
|
2581
|
+
details: item.details,
|
|
2582
|
+
formatters: bossToolFormatters
|
|
2583
|
+
}
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
function parseStatusGrade(text) {
|
|
2587
|
+
const matches = [
|
|
2588
|
+
...text.matchAll(/(?:^|\b)Status:\s*(DONE|UNVERIFIED|PARTIAL|BLOCKED|INFO)\b/gim)
|
|
2589
|
+
];
|
|
2590
|
+
const last = matches[matches.length - 1];
|
|
2591
|
+
if (!last) return null;
|
|
2592
|
+
return last[1].toUpperCase();
|
|
2593
|
+
}
|
|
2594
|
+
function statusGradeColor(grade, theme) {
|
|
2595
|
+
switch (grade) {
|
|
2596
|
+
case "DONE":
|
|
2597
|
+
return theme.success;
|
|
2598
|
+
case "UNVERIFIED":
|
|
2599
|
+
case "PARTIAL":
|
|
2600
|
+
return theme.warning;
|
|
2601
|
+
case "BLOCKED":
|
|
2602
|
+
return theme.error;
|
|
2603
|
+
case "INFO":
|
|
2604
|
+
return theme.textDim;
|
|
2605
|
+
default:
|
|
2606
|
+
return theme.textDim;
|
|
2407
2607
|
}
|
|
2408
2608
|
}
|
|
2409
|
-
function
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2609
|
+
function WorkerEventRow({ item }) {
|
|
2610
|
+
const theme = useTheme();
|
|
2611
|
+
const failedCount = item.toolsUsed.filter((t) => !t.ok).length;
|
|
2612
|
+
const grade = parseStatusGrade(item.finalText);
|
|
2613
|
+
const loaderStatus = grade === "BLOCKED" || failedCount > 0 ? "error" : grade === "UNVERIFIED" || grade === "PARTIAL" ? "queued" : "done";
|
|
2614
|
+
const headerColor = loaderStatus === "error" ? theme.toolError : projectColor(item.project);
|
|
2615
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "row", children: [
|
|
2616
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToolUseLoader, { status: loaderStatus }),
|
|
2617
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { wrap: "wrap", children: [
|
|
2618
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: headerColor, bold: true, children: item.project }),
|
|
2619
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: theme.text, children: ` turn ${item.turnIndex}` }),
|
|
2620
|
+
grade && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2621
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
|
|
2622
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: statusGradeColor(grade, theme), bold: true, children: grade })
|
|
2623
|
+
] })
|
|
2624
|
+
] }) })
|
|
2625
|
+
] });
|
|
2626
|
+
}
|
|
2627
|
+
function WorkerErrorRow({ item }) {
|
|
2628
|
+
const theme = useTheme();
|
|
2629
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
2630
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "row", children: [
|
|
2631
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToolUseLoader, { status: "error" }),
|
|
2632
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { wrap: "wrap", children: [
|
|
2633
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: theme.toolError, bold: true, children: item.project }),
|
|
2634
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: theme.textDim, children: " worker error" })
|
|
2635
|
+
] }) })
|
|
2636
|
+
] }),
|
|
2637
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: theme.error, wrap: "wrap", children: item.message }) })
|
|
2638
|
+
] });
|
|
2639
|
+
}
|
|
2640
|
+
function InfoRow({
|
|
2641
|
+
text,
|
|
2642
|
+
level
|
|
2643
|
+
}) {
|
|
2644
|
+
if (level === "info") return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AssistantMessage, { text });
|
|
2645
|
+
const theme = useTheme();
|
|
2646
|
+
const color2 = level === "error" ? theme.error : theme.warning;
|
|
2647
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "row", children: [
|
|
2648
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToolUseLoader, { status: level === "error" ? "error" : "queued" }),
|
|
2649
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: color2, wrap: "wrap", children: text }) })
|
|
2650
|
+
] });
|
|
2651
|
+
}
|
|
2652
|
+
function BossStreamingTurnView({
|
|
2653
|
+
turn,
|
|
2654
|
+
isRunning,
|
|
2655
|
+
liveItems = [],
|
|
2656
|
+
lastPendingHistoryItem,
|
|
2657
|
+
lastHistoryItem,
|
|
2658
|
+
availableTerminalHeight = 20
|
|
2659
|
+
}) {
|
|
2660
|
+
const visibleLiveItems = liveItems.filter(
|
|
2661
|
+
(item) => item.kind === "user" || item.kind === "tool_start" || item.kind === "tool_done" || item.kind === "tool_group" || item.kind === "compacting" || item.kind === "compacted"
|
|
2662
|
+
);
|
|
2663
|
+
const lastLiveItem = visibleLiveItems[visibleLiveItems.length - 1];
|
|
2664
|
+
const visibleStreamingText = turn?.text ?? "";
|
|
2665
|
+
const previousTranscriptItem = lastPendingHistoryItem ?? lastHistoryItem;
|
|
2666
|
+
const isAwaitingAssistantAfterUser = isRunning && visibleStreamingText.trim().length === 0 && (lastLiveItem?.kind === "user" || !lastLiveItem && previousTranscriptItem?.kind === "user");
|
|
2667
|
+
const shouldReserveStreamingSpacing = isRunning && (visibleStreamingText.trim().length > 0 || visibleLiveItems.some((item) => BOSS_SPACING_KINDS.has(item.kind)) || isAwaitingAssistantAfterUser);
|
|
2668
|
+
const assistantMarginTop = shouldTopSpaceStreamingAssistant({
|
|
2669
|
+
visibleStreamingText,
|
|
2670
|
+
lastLiveItem,
|
|
2671
|
+
lastPendingHistoryItem,
|
|
2672
|
+
lastHistoryItem,
|
|
2673
|
+
spacingKinds: BOSS_SPACING_KINDS,
|
|
2674
|
+
compactBoundaries: BOSS_COMPACT_BOUNDARIES
|
|
2675
|
+
}) ? 1 : 0;
|
|
2676
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2677
|
+
ChatLivePane,
|
|
2678
|
+
{
|
|
2679
|
+
liveItems: visibleLiveItems,
|
|
2680
|
+
renderItem: (_item, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2681
|
+
BossTranscriptRow,
|
|
2682
|
+
{
|
|
2683
|
+
row: visibleLiveItems[index],
|
|
2684
|
+
previousRow: index > 0 ? visibleLiveItems[index - 1] : previousTranscriptItem
|
|
2425
2685
|
}
|
|
2426
|
-
|
|
2427
|
-
|
|
2686
|
+
),
|
|
2687
|
+
isRunning,
|
|
2688
|
+
visibleStreamingText,
|
|
2689
|
+
streamingThinking: "",
|
|
2690
|
+
thinkingMs: turn?.thinkingMs ?? 0,
|
|
2691
|
+
reserveStreamingSpacing: shouldReserveStreamingSpacing,
|
|
2692
|
+
renderMarkdown: true,
|
|
2693
|
+
measuredLiveAreaRows: availableTerminalHeight,
|
|
2694
|
+
assistantMarginTop,
|
|
2695
|
+
streamingContinuation: false
|
|
2696
|
+
}
|
|
2697
|
+
);
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
// src/boss-terminal-history.tsx
|
|
2701
|
+
init_esm_shims();
|
|
2702
|
+
function createBossTerminalHistoryPrinter({
|
|
2703
|
+
stream = process.stdout
|
|
2704
|
+
} = {}) {
|
|
2705
|
+
const printed = /* @__PURE__ */ new Set();
|
|
2706
|
+
let previousPrintedKind = null;
|
|
2707
|
+
return {
|
|
2708
|
+
print(items, context, options) {
|
|
2709
|
+
const writeOutput = options?.write ?? ((data) => void stream.write(data));
|
|
2710
|
+
for (const item of items) {
|
|
2711
|
+
if (!options?.force && printed.has(item.id)) continue;
|
|
2712
|
+
const output = serializeBossItemToTerminalHistory(item, context);
|
|
2713
|
+
const formatted = formatHistoryWrite(output, {
|
|
2714
|
+
leadingSeparator: item.kind === "banner" ? false : shouldSeparateTranscriptItemKinds({
|
|
2715
|
+
previousKind: previousPrintedKind ?? void 0,
|
|
2716
|
+
currentKind: item.kind,
|
|
2717
|
+
spacingKinds: BOSS_SPACING_KINDS,
|
|
2718
|
+
compactBoundaries: BOSS_COMPACT_BOUNDARIES
|
|
2719
|
+
}),
|
|
2720
|
+
trailingBlankLine: item.kind === "banner",
|
|
2721
|
+
trailingNewlines: item.kind === "user" ? 1 : void 0
|
|
2722
|
+
});
|
|
2723
|
+
if (formatted.length === 0) continue;
|
|
2724
|
+
printed.add(item.id);
|
|
2725
|
+
writeOutput(formatted);
|
|
2726
|
+
previousPrintedKind = item.kind;
|
|
2428
2727
|
}
|
|
2728
|
+
},
|
|
2729
|
+
clear() {
|
|
2730
|
+
printed.clear();
|
|
2731
|
+
previousPrintedKind = null;
|
|
2732
|
+
},
|
|
2733
|
+
resetPrinted() {
|
|
2734
|
+
printed.clear();
|
|
2735
|
+
previousPrintedKind = null;
|
|
2736
|
+
},
|
|
2737
|
+
get printedIds() {
|
|
2738
|
+
return printed;
|
|
2429
2739
|
}
|
|
2430
|
-
|
|
2431
|
-
if (shouldCheck) scheduleBackgroundCheck(currentVersion);
|
|
2432
|
-
return message;
|
|
2433
|
-
} catch {
|
|
2434
|
-
return null;
|
|
2435
|
-
}
|
|
2740
|
+
};
|
|
2436
2741
|
}
|
|
2437
|
-
function
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2742
|
+
function serializeBossItemToTerminalHistory(item, context) {
|
|
2743
|
+
switch (item.kind) {
|
|
2744
|
+
case "banner":
|
|
2745
|
+
return renderBanner(context);
|
|
2746
|
+
case "worker_event":
|
|
2747
|
+
return renderWorkerEvent(item, context);
|
|
2748
|
+
case "worker_error":
|
|
2749
|
+
return renderWorkerError(item, context);
|
|
2750
|
+
case "task_dispatch":
|
|
2751
|
+
return renderTaskDispatch(item, context);
|
|
2752
|
+
case "tool_group":
|
|
2753
|
+
return renderToolGroup(item, context);
|
|
2754
|
+
case "update_notice":
|
|
2755
|
+
return renderUpdateNotice(item, context);
|
|
2756
|
+
default:
|
|
2757
|
+
return serializeCompletedItemToTerminalHistory(toGGCoderCompletedItem(item), context);
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
function renderBanner(context) {
|
|
2761
|
+
const logo = LOGO_LINES.map((lineText) => gradientLine(lineText, GRADIENT));
|
|
2762
|
+
const shortcuts = `${color(COLORS.primary, "^T")} ${dim(context, "tasks ")}${color(
|
|
2763
|
+
COLORS.primary,
|
|
2764
|
+
"Tab"
|
|
2765
|
+
)} ${dim(context, "scope ")}${color(COLORS.primary, "\u21E7Tab")} ${dim(
|
|
2766
|
+
context,
|
|
2767
|
+
"thinking "
|
|
2768
|
+
)}${color(COLORS.primary, "ESC")} ${dim(context, "interrupt")}`;
|
|
2769
|
+
return [
|
|
2770
|
+
"",
|
|
2771
|
+
`${logo[0]}${LOGO_GAP}${color(COLORS.primary, BRAND, true)}${dim(
|
|
2772
|
+
context,
|
|
2773
|
+
` v${VERSION} \xB7 By `
|
|
2774
|
+
)}${color(COLORS.text, AUTHOR, true)}`,
|
|
2775
|
+
`${logo[1]}${LOGO_GAP}${color(COLORS.accent, "Orchestrator")}`,
|
|
2776
|
+
`${logo[2]}${LOGO_GAP}${shortcuts}`,
|
|
2777
|
+
""
|
|
2778
|
+
].join("\n");
|
|
2446
2779
|
}
|
|
2447
|
-
function
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2780
|
+
function renderUpdateNotice(item, context) {
|
|
2781
|
+
return renderRoundNoticeBox(
|
|
2782
|
+
[`${color(COLORS.accent, "\u2728 ", true)}${color(COLORS.primary, item.text, true)}`],
|
|
2783
|
+
context,
|
|
2784
|
+
COLORS.accent
|
|
2785
|
+
);
|
|
2786
|
+
}
|
|
2787
|
+
function renderToolGroup(item, context) {
|
|
2788
|
+
const tools = item.tools;
|
|
2789
|
+
const allDone = tools.every((tool) => tool.status === "done");
|
|
2790
|
+
const hasError = tools.some((tool) => tool.isError);
|
|
2791
|
+
const status = allDone ? hasError ? "error" : "done" : "running";
|
|
2792
|
+
const label = buildToolGroupSummary(tools, allDone, bossToolGroupRenderers).map((seg) => {
|
|
2793
|
+
const hex = seg.tone ? toolTonePalette(context.theme, seg.tone).primary : context.theme.toolName;
|
|
2794
|
+
return color(hex, seg.text, seg.bold);
|
|
2795
|
+
}).join("");
|
|
2796
|
+
return toolStatusHeader({ status, label, context, labelAlreadyStyled: true });
|
|
2797
|
+
}
|
|
2798
|
+
function renderWorkerEvent(item, context) {
|
|
2799
|
+
const theme = context.theme;
|
|
2800
|
+
const failedCount = item.toolsUsed.filter((tool) => !tool.ok).length;
|
|
2801
|
+
const grade = parseStatusGrade(item.finalText);
|
|
2802
|
+
const isError = grade === "BLOCKED" || failedCount > 0;
|
|
2803
|
+
const isWarning = grade === "UNVERIFIED" || grade === "PARTIAL";
|
|
2804
|
+
const statusColor = isError ? theme.error : isWarning ? theme.warning : theme.success;
|
|
2805
|
+
const headerColor = isError ? theme.error : projectColor(item.project);
|
|
2806
|
+
return toolStatusHeader({
|
|
2807
|
+
status: isError ? "error" : isWarning ? "queued" : "done",
|
|
2808
|
+
label: color(headerColor, item.project, true),
|
|
2809
|
+
suffix: `${color(theme.text, ` turn ${item.turnIndex}`)}${grade ? `${dim(context, " \xB7 ")}${color(statusColor, grade, true)}` : ""}`,
|
|
2810
|
+
context,
|
|
2811
|
+
labelAlreadyStyled: true
|
|
2459
2812
|
});
|
|
2460
2813
|
}
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
latestVersion,
|
|
2473
|
-
updatePending: true
|
|
2474
|
-
});
|
|
2475
|
-
onUpdate(
|
|
2476
|
-
`Ken just pushed a fresh update \u2014 ${currentVersion} \u2192 ${latestVersion}! Restart ggboss to grab it (or run ${info.updateCommand} if you can't wait).`
|
|
2477
|
-
);
|
|
2478
|
-
stopPeriodicUpdateCheck();
|
|
2479
|
-
}).catch(() => {
|
|
2480
|
-
});
|
|
2481
|
-
}, CHECK_INTERVAL_MS);
|
|
2482
|
-
periodicTimer.unref();
|
|
2814
|
+
function renderWorkerError(item, context) {
|
|
2815
|
+
return [
|
|
2816
|
+
toolStatusHeader({
|
|
2817
|
+
status: "error",
|
|
2818
|
+
label: color(context.theme.error, item.project, true),
|
|
2819
|
+
suffix: dim(context, " worker error"),
|
|
2820
|
+
context,
|
|
2821
|
+
labelAlreadyStyled: true
|
|
2822
|
+
}),
|
|
2823
|
+
messageResponseText(color(context.theme.error, item.message), context)
|
|
2824
|
+
].join("\n");
|
|
2483
2825
|
}
|
|
2484
|
-
function
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2826
|
+
function renderTaskDispatch(item, context) {
|
|
2827
|
+
const count = item.tasks.length;
|
|
2828
|
+
const lines = [
|
|
2829
|
+
toolStatusHeader({
|
|
2830
|
+
status: "done",
|
|
2831
|
+
label: color(context.theme.text, `Running ${count} task${count === 1 ? "" : "s"}:`, true),
|
|
2832
|
+
context,
|
|
2833
|
+
dotColor: COLORS.primary,
|
|
2834
|
+
labelAlreadyStyled: true
|
|
2835
|
+
})
|
|
2836
|
+
];
|
|
2837
|
+
for (const task of item.tasks) {
|
|
2838
|
+
lines.push(
|
|
2839
|
+
` \u2022 ${color(projectColor(task.project), task.project, true)}${dim(context, ": ")}${color(context.theme.text, task.title)}`
|
|
2840
|
+
);
|
|
2488
2841
|
}
|
|
2842
|
+
return lines.join("\n");
|
|
2489
2843
|
}
|
|
2844
|
+
function toGGCoderCompletedItem(item) {
|
|
2845
|
+
switch (item.kind) {
|
|
2846
|
+
case "user":
|
|
2847
|
+
return { kind: "user", id: item.id, text: item.text };
|
|
2848
|
+
case "assistant":
|
|
2849
|
+
return {
|
|
2850
|
+
kind: "assistant",
|
|
2851
|
+
id: item.id,
|
|
2852
|
+
text: item.text,
|
|
2853
|
+
thinking: item.thinking,
|
|
2854
|
+
thinkingMs: item.thinkingMs,
|
|
2855
|
+
continuation: item.continuation
|
|
2856
|
+
};
|
|
2857
|
+
case "tool_start":
|
|
2858
|
+
return {
|
|
2859
|
+
kind: "tool_start",
|
|
2860
|
+
id: item.id,
|
|
2861
|
+
toolCallId: item.toolCallId,
|
|
2862
|
+
name: item.name,
|
|
2863
|
+
args: formatBossToolArgsForHistory(item.name, item.args),
|
|
2864
|
+
startedAt: item.startedAt,
|
|
2865
|
+
animateUntil: item.animateUntil,
|
|
2866
|
+
progressOutput: item.progressOutput
|
|
2867
|
+
};
|
|
2868
|
+
case "tool_done":
|
|
2869
|
+
return {
|
|
2870
|
+
kind: "tool_done",
|
|
2871
|
+
id: item.id,
|
|
2872
|
+
name: item.name,
|
|
2873
|
+
args: formatBossToolArgsForHistory(item.name, item.args),
|
|
2874
|
+
result: formatBossToolResultForHistory(item.name, item.result, item.isError),
|
|
2875
|
+
isError: item.isError,
|
|
2876
|
+
durationMs: item.durationMs,
|
|
2877
|
+
details: item.details
|
|
2878
|
+
};
|
|
2879
|
+
case "info":
|
|
2880
|
+
return { kind: "info", id: item.id, text: item.text };
|
|
2881
|
+
case "compacting":
|
|
2882
|
+
return { kind: "compacting", id: item.id };
|
|
2883
|
+
case "compacted":
|
|
2884
|
+
return {
|
|
2885
|
+
kind: "compacted",
|
|
2886
|
+
id: item.id,
|
|
2887
|
+
originalCount: item.originalCount,
|
|
2888
|
+
newCount: item.newCount,
|
|
2889
|
+
tokensBefore: item.tokensBefore,
|
|
2890
|
+
tokensAfter: item.tokensAfter
|
|
2891
|
+
};
|
|
2892
|
+
case "stopped":
|
|
2893
|
+
return { kind: "stopped", id: item.id, text: item.text };
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
function formatBossToolArgsForHistory(name, args) {
|
|
2897
|
+
switch (name) {
|
|
2898
|
+
case "list_workers":
|
|
2899
|
+
return { action: "workers" };
|
|
2900
|
+
case "get_worker_status":
|
|
2901
|
+
case "get_worker_summary":
|
|
2902
|
+
return { action: String(args.project ?? "") };
|
|
2903
|
+
case "prompt_worker": {
|
|
2904
|
+
const project = String(args.project ?? "");
|
|
2905
|
+
const message = String(args.message ?? "").replace(/\s+/gu, " ");
|
|
2906
|
+
const fresh = args.fresh === true ? "fresh \xB7 " : "";
|
|
2907
|
+
const detail = project ? `${fresh}${project} \xB7 ${message}` : `${fresh}${message}`;
|
|
2908
|
+
return {
|
|
2909
|
+
action: truncatePlain(detail, Math.max(20, contextlessPromptWorkerDetailLen(project)))
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2912
|
+
default:
|
|
2913
|
+
return args;
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
function formatBossToolResultForHistory(name, result, isError) {
|
|
2917
|
+
if (isError) return result;
|
|
2918
|
+
const inline = formatBossToolInlineForHistory(name, result);
|
|
2919
|
+
if (!inline) return result;
|
|
2920
|
+
return typeof inline === "string" ? inline : inline.text;
|
|
2921
|
+
}
|
|
2922
|
+
function formatBossToolInlineForHistory(name, result) {
|
|
2923
|
+
switch (name) {
|
|
2924
|
+
case "list_workers": {
|
|
2925
|
+
const lines = result.split("\n").filter((lineText) => lineText.startsWith("-"));
|
|
2926
|
+
return `${lines.length} worker${lines.length === 1 ? "" : "s"}`;
|
|
2927
|
+
}
|
|
2928
|
+
case "prompt_worker": {
|
|
2929
|
+
if (result.includes("currently working")) return "busy \u2014 skipped";
|
|
2930
|
+
if (result.includes("Unknown project")) return "unknown project";
|
|
2931
|
+
return "dispatched";
|
|
2932
|
+
}
|
|
2933
|
+
case "get_worker_status": {
|
|
2934
|
+
const parts = result.split(":");
|
|
2935
|
+
if (parts.length < 2) return void 0;
|
|
2936
|
+
return parts.slice(1).join(":").trim();
|
|
2937
|
+
}
|
|
2938
|
+
case "get_worker_summary": {
|
|
2939
|
+
const turnMatch = result.match(/Turn:\s*(\d+)/u);
|
|
2940
|
+
const toolsMatch = result.match(/Tools used:\s*(.+)/u);
|
|
2941
|
+
const tools = toolsMatch ? toolsMatch[1] : "";
|
|
2942
|
+
const toolCount = tools && tools !== "(no tools used)" ? tools.split(",").length : 0;
|
|
2943
|
+
const turn = turnMatch ? `turn ${turnMatch[1]}` : void 0;
|
|
2944
|
+
const toolSummary = toolCount > 0 ? `${toolCount} tool${toolCount === 1 ? "" : "s"}` : void 0;
|
|
2945
|
+
return [turn, toolSummary].filter(Boolean).join(" \xB7 ") || void 0;
|
|
2946
|
+
}
|
|
2947
|
+
default:
|
|
2948
|
+
return void 0;
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
function contextlessPromptWorkerDetailLen(project) {
|
|
2952
|
+
const cols = process.stdout.columns ?? 80;
|
|
2953
|
+
const fixed = 2 + 13 + 1 + project.length + 3 + 1 + 1 + 11 + 6;
|
|
2954
|
+
return Math.max(20, cols - fixed);
|
|
2955
|
+
}
|
|
2956
|
+
function toolStatusHeader({
|
|
2957
|
+
status,
|
|
2958
|
+
label,
|
|
2959
|
+
suffix = "",
|
|
2960
|
+
context,
|
|
2961
|
+
dotColor,
|
|
2962
|
+
labelAlreadyStyled = false
|
|
2963
|
+
}) {
|
|
2964
|
+
const resolvedDotColor = dotColor ?? (status === "error" ? context.theme.error : status === "done" ? context.theme.success : status === "queued" ? context.theme.warning : context.theme.spinnerColor);
|
|
2965
|
+
const indicator = status === "running" ? "\u280B" : "\u23FA";
|
|
2966
|
+
const labelText = labelAlreadyStyled ? label : color(context.theme.toolName, label, true);
|
|
2967
|
+
return `${RESPONSE_LEFT_PADDING}${color(resolvedDotColor, indicator)} ${labelText}${suffix}`;
|
|
2968
|
+
}
|
|
2969
|
+
function messageResponseText(text, context) {
|
|
2970
|
+
const [first, ...rest] = wrapPlain(text, Math.max(10, context.columns - 8)).split("\n");
|
|
2971
|
+
return [
|
|
2972
|
+
`${RESPONSE_LEFT_PADDING}${dim(context, " \u23BF ")}${first ?? ""}`,
|
|
2973
|
+
...rest.map((lineText) => `${RESPONSE_LEFT_PADDING}${dim(context, " ")}${lineText}`)
|
|
2974
|
+
].join("\n");
|
|
2975
|
+
}
|
|
2976
|
+
function renderRoundNoticeBox(lines, context, borderColor) {
|
|
2977
|
+
const width = Math.max(
|
|
2978
|
+
4,
|
|
2979
|
+
Math.min(
|
|
2980
|
+
context.columns - RESPONSE_LEFT_PADDING.length,
|
|
2981
|
+
Math.max(...lines.map((lineText) => stripAnsi(lineText).length)) + 4
|
|
2982
|
+
)
|
|
2983
|
+
);
|
|
2984
|
+
const contentWidth = Math.max(1, width - 4);
|
|
2985
|
+
const top = `${color(borderColor, "\u256D")}${color(borderColor, "\u2500".repeat(width - 2))}${color(
|
|
2986
|
+
borderColor,
|
|
2987
|
+
"\u256E"
|
|
2988
|
+
)}`;
|
|
2989
|
+
const bottom = `${color(borderColor, "\u2570")}${color(borderColor, "\u2500".repeat(width - 2))}${color(
|
|
2990
|
+
borderColor,
|
|
2991
|
+
"\u256F"
|
|
2992
|
+
)}`;
|
|
2993
|
+
const body = lines.flatMap((lineText) => wrapPlain(lineText, contentWidth).split("\n")).map((lineText) => {
|
|
2994
|
+
const plainLength = stripAnsi(lineText).length;
|
|
2995
|
+
return `${color(borderColor, "\u2502")} ${lineText}${" ".repeat(Math.max(0, contentWidth - plainLength))} ${color(borderColor, "\u2502")}`;
|
|
2996
|
+
});
|
|
2997
|
+
return indent([top, ...body, bottom].join("\n"), RESPONSE_LEFT_PADDING);
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
// src/auto-update.ts
|
|
3001
|
+
init_esm_shims();
|
|
3002
|
+
import path4 from "path";
|
|
3003
|
+
import os2 from "os";
|
|
3004
|
+
var updater = createAutoUpdater({
|
|
3005
|
+
packageName: "@kenkaiiii/gg-boss",
|
|
3006
|
+
stateFilePath: () => path4.join(os2.homedir(), ".gg", "boss", "update-state.json"),
|
|
3007
|
+
periodicMessage: ({ currentVersion, latestVersion, updateCommand }) => `Ken just pushed a fresh update \u2014 ${currentVersion} \u2192 ${latestVersion}! Restart ggboss to grab it (or run ${updateCommand} if you can't wait).`
|
|
3008
|
+
});
|
|
3009
|
+
var checkAndAutoUpdate = updater.checkAndAutoUpdate;
|
|
3010
|
+
var getPendingUpdate = updater.getPendingUpdate;
|
|
3011
|
+
var startPeriodicUpdateCheck = updater.startPeriodicUpdateCheck;
|
|
3012
|
+
var stopPeriodicUpdateCheck = updater.stopPeriodicUpdateCheck;
|
|
2490
3013
|
|
|
2491
3014
|
// src/orchestrator-app.tsx
|
|
2492
|
-
var
|
|
3015
|
+
var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
|
|
2493
3016
|
function BossApp(props) {
|
|
2494
3017
|
const theme = loadTheme("dark");
|
|
2495
|
-
return /* @__PURE__ */ (0,
|
|
3018
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TerminalSizeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AnimationProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(BossAppInner, { ...props }) }) }) });
|
|
2496
3019
|
}
|
|
2497
|
-
function BossAppInner({ boss, resetUI }) {
|
|
3020
|
+
function BossAppInner({ boss, resetUI, terminalHistoryPrinter }) {
|
|
2498
3021
|
const state = useBossState();
|
|
3022
|
+
const theme = useTheme();
|
|
2499
3023
|
const { exit } = use_app_default();
|
|
2500
|
-
const { stdout } = use_stdout_default();
|
|
2501
|
-
const {
|
|
2502
|
-
const runStartRef = (0,
|
|
3024
|
+
const { stdout, write: writeStdout } = use_stdout_default();
|
|
3025
|
+
const { columns, rows } = useTerminalSize();
|
|
3026
|
+
const runStartRef = (0, import_react14.useRef)(null);
|
|
2503
3027
|
runStartRef.current = state.runStartMs;
|
|
2504
|
-
const charCountRef = (0,
|
|
3028
|
+
const charCountRef = (0, import_react14.useRef)(0);
|
|
2505
3029
|
charCountRef.current = state.streaming?.text.length ?? 0;
|
|
2506
|
-
const realTokensAccumRef = (0,
|
|
3030
|
+
const realTokensAccumRef = (0, import_react14.useRef)(0);
|
|
2507
3031
|
realTokensAccumRef.current = state.bossInputTokens;
|
|
2508
|
-
const [lastUserMessage, setLastUserMessage] = (0,
|
|
3032
|
+
const [lastUserMessage, setLastUserMessage] = (0, import_react14.useState)("");
|
|
2509
3033
|
const overlay = state.overlay;
|
|
2510
|
-
const [currentRadio, setCurrentRadio] = (0,
|
|
2511
|
-
const [updatePending, setUpdatePending] = (0,
|
|
3034
|
+
const [currentRadio, setCurrentRadio] = (0, import_react14.useState)(() => getCurrentStation());
|
|
3035
|
+
const [updatePending, setUpdatePending] = (0, import_react14.useState)(
|
|
2512
3036
|
() => getPendingUpdate(VERSION) !== null
|
|
2513
3037
|
);
|
|
2514
|
-
(0,
|
|
3038
|
+
(0, import_react14.useEffect)(() => {
|
|
2515
3039
|
startPeriodicUpdateCheck(VERSION, (msg) => {
|
|
2516
3040
|
bossStore.appendUpdateNotice(msg);
|
|
2517
3041
|
setUpdatePending(true);
|
|
@@ -2519,8 +3043,8 @@ function BossAppInner({ boss, resetUI }) {
|
|
|
2519
3043
|
return () => stopPeriodicUpdateCheck();
|
|
2520
3044
|
}, []);
|
|
2521
3045
|
const workersRunning = state.workers.filter((w) => w.status === "working").length;
|
|
2522
|
-
const titlePrevRef = (0,
|
|
2523
|
-
(0,
|
|
3046
|
+
const titlePrevRef = (0, import_react14.useRef)("");
|
|
3047
|
+
(0, import_react14.useEffect)(() => {
|
|
2524
3048
|
if (!stdout) return;
|
|
2525
3049
|
let title;
|
|
2526
3050
|
if (workersRunning > 0) {
|
|
@@ -2536,45 +3060,83 @@ function BossAppInner({ boss, resetUI }) {
|
|
|
2536
3060
|
stdout.write(`\x1B]0;${title}\x1B\\`);
|
|
2537
3061
|
}
|
|
2538
3062
|
}, [stdout, workersRunning, state.phase]);
|
|
2539
|
-
(0,
|
|
3063
|
+
(0, import_react14.useEffect)(() => {
|
|
2540
3064
|
return () => {
|
|
2541
3065
|
stdout?.write(`\x1B]0;GG Boss\x1B\\`);
|
|
2542
3066
|
};
|
|
2543
3067
|
}, [stdout]);
|
|
2544
|
-
const
|
|
2545
|
-
|
|
2546
|
-
|
|
3068
|
+
const liveItems = state.liveItems;
|
|
3069
|
+
const terminalHistoryPrinterRef = (0, import_react14.useRef)(
|
|
3070
|
+
terminalHistoryPrinter ?? createBossTerminalHistoryPrinter({ stream: stdout })
|
|
2547
3071
|
);
|
|
2548
|
-
const
|
|
3072
|
+
const terminalHistoryContext = {
|
|
3073
|
+
theme,
|
|
3074
|
+
columns,
|
|
3075
|
+
version: VERSION,
|
|
3076
|
+
model: state.bossModel,
|
|
3077
|
+
provider: state.bossProvider,
|
|
3078
|
+
cwd: process.cwd()
|
|
3079
|
+
};
|
|
3080
|
+
const printedHistoryIdsRef = (0, import_react14.useRef)(/* @__PURE__ */ new Set());
|
|
3081
|
+
(0, import_react14.useEffect)(() => {
|
|
3082
|
+
const printer = terminalHistoryPrinterRef.current;
|
|
3083
|
+
const pending = state.history.filter((item) => !printedHistoryIdsRef.current.has(item.id));
|
|
3084
|
+
if (pending.length === 0) return;
|
|
3085
|
+
printer.print(pending, terminalHistoryContext, { write: writeStdout });
|
|
3086
|
+
for (const item of pending) printedHistoryIdsRef.current.add(item.id);
|
|
3087
|
+
}, [columns, state.bossModel, state.bossProvider, state.history, theme, writeStdout]);
|
|
3088
|
+
const overlayResetTimerRef = (0, import_react14.useRef)(null);
|
|
3089
|
+
(0, import_react14.useEffect)(() => {
|
|
3090
|
+
return () => {
|
|
3091
|
+
if (overlayResetTimerRef.current) clearTimeout(overlayResetTimerRef.current);
|
|
3092
|
+
};
|
|
3093
|
+
}, []);
|
|
3094
|
+
const scheduleOverlayReset = (0, import_react14.useCallback)(() => {
|
|
3095
|
+
if (!resetUI) return;
|
|
3096
|
+
if (overlayResetTimerRef.current) clearTimeout(overlayResetTimerRef.current);
|
|
3097
|
+
overlayResetTimerRef.current = setTimeout(() => {
|
|
3098
|
+
overlayResetTimerRef.current = null;
|
|
3099
|
+
resetUI();
|
|
3100
|
+
}, 0);
|
|
3101
|
+
}, [resetUI]);
|
|
3102
|
+
const openOverlay = (0, import_react14.useCallback)(
|
|
2549
3103
|
(next) => {
|
|
2550
3104
|
bossStore.setOverlay(next);
|
|
2551
|
-
|
|
3105
|
+
scheduleOverlayReset();
|
|
2552
3106
|
},
|
|
2553
|
-
[
|
|
3107
|
+
[scheduleOverlayReset]
|
|
2554
3108
|
);
|
|
2555
|
-
const closeOverlay = (0,
|
|
3109
|
+
const closeOverlay = (0, import_react14.useCallback)(() => {
|
|
2556
3110
|
bossStore.setOverlay(null);
|
|
2557
|
-
|
|
2558
|
-
}, [
|
|
3111
|
+
scheduleOverlayReset();
|
|
3112
|
+
}, [scheduleOverlayReset]);
|
|
2559
3113
|
void stdout;
|
|
2560
3114
|
const handleDoubleExit = useDoublePress(
|
|
2561
3115
|
(pending) => bossStore.setExitPending(pending),
|
|
2562
3116
|
() => exit()
|
|
2563
3117
|
);
|
|
2564
|
-
(0,
|
|
3118
|
+
(0, import_react14.useEffect)(() => {
|
|
2565
3119
|
if (state.pendingFlush.length > 0) {
|
|
2566
3120
|
bossStore.commitPendingFlush();
|
|
2567
3121
|
}
|
|
2568
3122
|
}, [state.flushGeneration, state.pendingFlush.length]);
|
|
3123
|
+
const handleAbort = (0, import_react14.useCallback)(() => {
|
|
3124
|
+
if (state.phase === "working") {
|
|
3125
|
+
boss.abort();
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
handleDoubleExit();
|
|
3129
|
+
}, [boss, handleDoubleExit, state.phase]);
|
|
2569
3130
|
use_input_default((input, key) => {
|
|
3131
|
+
if (key.ctrl && input === "c" && overlay) {
|
|
3132
|
+
handleAbort();
|
|
3133
|
+
return;
|
|
3134
|
+
}
|
|
2570
3135
|
if (key.ctrl && input === "t") {
|
|
2571
3136
|
if (overlay === "tasks") closeOverlay();
|
|
2572
3137
|
else openOverlay("tasks");
|
|
2573
3138
|
return;
|
|
2574
3139
|
}
|
|
2575
|
-
if (key.escape && state.phase === "working") {
|
|
2576
|
-
boss.abort();
|
|
2577
|
-
}
|
|
2578
3140
|
});
|
|
2579
3141
|
const handleSlashCommand = async (value) => {
|
|
2580
3142
|
const parsed = parseSlash(value);
|
|
@@ -2591,7 +3153,7 @@ function BossAppInner({ boss, resetUI }) {
|
|
|
2591
3153
|
return true;
|
|
2592
3154
|
case "clear":
|
|
2593
3155
|
bossStore.clearHistory();
|
|
2594
|
-
resetUI?.();
|
|
3156
|
+
resetUI?.("session-clear");
|
|
2595
3157
|
await boss.resetConversation();
|
|
2596
3158
|
bossStore.appendInfo("Session cleared.", "info");
|
|
2597
3159
|
return true;
|
|
@@ -2622,11 +3184,11 @@ function BossAppInner({ boss, resetUI }) {
|
|
|
2622
3184
|
}
|
|
2623
3185
|
const provider = value.slice(0, colon);
|
|
2624
3186
|
const model = value.slice(colon + 1);
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
}
|
|
3187
|
+
const switchPromise = overlay === "model-boss" ? boss.switchBossModel(provider, model) : overlay === "model-workers" ? boss.switchWorkerModel(provider, model) : Promise.resolve();
|
|
3188
|
+
void switchPromise.catch((err) => {
|
|
3189
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3190
|
+
bossStore.appendInfo(`Model switch failed: ${message}`, "error");
|
|
3191
|
+
});
|
|
2630
3192
|
closeOverlay();
|
|
2631
3193
|
};
|
|
2632
3194
|
const handleSubmit = (value) => {
|
|
@@ -2636,141 +3198,117 @@ function BossAppInner({ boss, resetUI }) {
|
|
|
2636
3198
|
void handleSlashCommand(trimmed);
|
|
2637
3199
|
return;
|
|
2638
3200
|
}
|
|
2639
|
-
bossStore.
|
|
3201
|
+
const userItem = bossStore.createUserItem(trimmed);
|
|
3202
|
+
terminalHistoryPrinterRef.current.print([userItem], terminalHistoryContext, {
|
|
3203
|
+
write: writeStdout
|
|
3204
|
+
});
|
|
3205
|
+
printedHistoryIdsRef.current.add(userItem.id);
|
|
3206
|
+
bossStore.queueSubmittedUserItem(userItem);
|
|
2640
3207
|
setLastUserMessage(trimmed);
|
|
2641
3208
|
const scoped = scopePrefix2(state.scope) + trimmed;
|
|
2642
3209
|
boss.enqueueUserMessage(scoped);
|
|
2643
3210
|
};
|
|
2644
|
-
const
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
};
|
|
3211
|
+
const activityVisible = state.phase === "working" && state.activityPhase !== "idle";
|
|
3212
|
+
const stallStatusVisible = false;
|
|
3213
|
+
const doneStatus = null;
|
|
3214
|
+
const statusSlotVisible = activityVisible || stallStatusVisible || !!doneStatus;
|
|
3215
|
+
const controlsRows = 7 + (statusSlotVisible ? 1 : 0) + (state.exitPending ? 0 : 1);
|
|
3216
|
+
const availableLiveRows = Math.max(1, rows - controlsRows);
|
|
2651
3217
|
if (rows < 14) {
|
|
2652
|
-
return /* @__PURE__ */ (0,
|
|
2653
|
-
/* @__PURE__ */ (0,
|
|
2654
|
-
/* @__PURE__ */ (0,
|
|
3218
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { flexDirection: "column", width: columns, paddingX: 1, marginTop: 1, children: [
|
|
3219
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { bold: true, color: COLORS.accent, children: "Terminal too small" }),
|
|
3220
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: COLORS.primary, children: `Resize to at least 14 rows to use GG Boss (currently ${rows}).` })
|
|
2655
3221
|
] });
|
|
2656
3222
|
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
),
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
onCancel: closeOverlay,
|
|
2721
|
-
onSelect: (value) => {
|
|
2722
|
-
if (value === "off") {
|
|
2723
|
-
stopRadio();
|
|
2724
|
-
setCurrentRadio(null);
|
|
2725
|
-
bossStore.appendInfo("Radio off.", "info");
|
|
2726
|
-
} else {
|
|
2727
|
-
const result = playRadio(value);
|
|
2728
|
-
if (result.ok) {
|
|
2729
|
-
setCurrentRadio(value);
|
|
2730
|
-
const station = RADIO_STATIONS.find((s) => s.id === value);
|
|
2731
|
-
bossStore.appendInfo(`Now playing: ${station?.name ?? value}`, "info");
|
|
2732
|
-
} else {
|
|
2733
|
-
bossStore.appendInfo(result.error ?? "Radio failed to start.", "warning");
|
|
2734
|
-
}
|
|
2735
|
-
}
|
|
2736
|
-
closeOverlay();
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2740
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2741
|
-
BossFooter,
|
|
2742
|
-
{
|
|
2743
|
-
bossModel: state.bossModel,
|
|
2744
|
-
workerModel: state.workerModel,
|
|
2745
|
-
tokensIn: state.bossInputTokens,
|
|
2746
|
-
exitPending: state.exitPending,
|
|
2747
|
-
bossThinkingLevel: state.bossThinkingLevel,
|
|
2748
|
-
updatePending,
|
|
2749
|
-
currentRadioStationId: currentRadio
|
|
2750
|
-
}
|
|
2751
|
-
),
|
|
2752
|
-
!state.exitPending && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2753
|
-
WorkerStatusBar,
|
|
2754
|
-
{
|
|
2755
|
-
workers: state.workers,
|
|
2756
|
-
pendingMessages: state.pendingUserMessages
|
|
3223
|
+
const lastPendingHistoryItem = state.pendingFlush[state.pendingFlush.length - 1];
|
|
3224
|
+
const lastHistoryItem = state.history[state.history.length - 1];
|
|
3225
|
+
const livePane = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3226
|
+
BossStreamingTurnView,
|
|
3227
|
+
{
|
|
3228
|
+
turn: state.streaming,
|
|
3229
|
+
isRunning: state.phase === "working",
|
|
3230
|
+
liveItems,
|
|
3231
|
+
lastPendingHistoryItem,
|
|
3232
|
+
lastHistoryItem,
|
|
3233
|
+
availableTerminalHeight: availableLiveRows
|
|
3234
|
+
}
|
|
3235
|
+
);
|
|
3236
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3237
|
+
BossChatScreen,
|
|
3238
|
+
{
|
|
3239
|
+
boss,
|
|
3240
|
+
columns,
|
|
3241
|
+
state,
|
|
3242
|
+
overlay,
|
|
3243
|
+
livePane,
|
|
3244
|
+
theme,
|
|
3245
|
+
statusSlotVisible,
|
|
3246
|
+
activityVisible,
|
|
3247
|
+
stallStatusVisible,
|
|
3248
|
+
doneStatus,
|
|
3249
|
+
elapsedMs: state.runStartMs ? Date.now() - state.runStartMs : 0,
|
|
3250
|
+
runStartRef,
|
|
3251
|
+
charCountRef,
|
|
3252
|
+
realTokensAccumRef,
|
|
3253
|
+
lastUserMessage,
|
|
3254
|
+
activeToolNames: (state.streaming?.tools ?? []).filter((tool) => tool.status === "running").map((tool) => tool.name),
|
|
3255
|
+
inputActive: !overlay,
|
|
3256
|
+
isRunning: state.phase === "working",
|
|
3257
|
+
onSubmit: handleSubmit,
|
|
3258
|
+
onAbort: handleAbort,
|
|
3259
|
+
onTab: () => bossStore.cycleScope(),
|
|
3260
|
+
onShiftTab: () => {
|
|
3261
|
+
const next = getNextThinkingLevel(
|
|
3262
|
+
state.bossProvider,
|
|
3263
|
+
state.bossModel,
|
|
3264
|
+
state.bossThinkingLevel
|
|
3265
|
+
);
|
|
3266
|
+
void boss.setBossThinking(next);
|
|
3267
|
+
},
|
|
3268
|
+
commands: BOSS_SLASH_COMMANDS,
|
|
3269
|
+
scopeBadge: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ScopePill, { scope: state.scope }),
|
|
3270
|
+
onCloseOverlay: closeOverlay,
|
|
3271
|
+
onModelSelect: handleModelSelect,
|
|
3272
|
+
currentRadio,
|
|
3273
|
+
onRadioSelect: (value) => {
|
|
3274
|
+
if (value === "off") {
|
|
3275
|
+
stopRadio();
|
|
3276
|
+
setCurrentRadio(null);
|
|
3277
|
+
bossStore.appendInfo("Radio off.", "info");
|
|
3278
|
+
} else {
|
|
3279
|
+
const result = playRadio(value);
|
|
3280
|
+
if (result.ok) {
|
|
3281
|
+
setCurrentRadio(value);
|
|
3282
|
+
const station = RADIO_STATIONS.find((stationInfo) => stationInfo.id === value);
|
|
3283
|
+
bossStore.appendInfo(`Now playing: ${station?.name ?? value}`, "info");
|
|
3284
|
+
} else {
|
|
3285
|
+
bossStore.appendInfo(result.error ?? "Radio failed to start.", "warning");
|
|
2757
3286
|
}
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
3287
|
+
}
|
|
3288
|
+
closeOverlay();
|
|
3289
|
+
},
|
|
3290
|
+
bossModel: state.bossModel,
|
|
3291
|
+
workerModel: state.workerModel,
|
|
3292
|
+
updatePending,
|
|
3293
|
+
currentRadioStationId: currentRadio,
|
|
3294
|
+
radioStations: RADIO_STATIONS,
|
|
3295
|
+
workers: state.workers,
|
|
3296
|
+
pendingMessages: state.pendingUserMessages,
|
|
3297
|
+
formatDuration: formatBossDuration
|
|
3298
|
+
}
|
|
3299
|
+
);
|
|
2762
3300
|
}
|
|
2763
3301
|
function ScopePill({ scope }) {
|
|
2764
3302
|
const theme = useTheme();
|
|
2765
3303
|
const isAll = scope === "all";
|
|
2766
3304
|
const bg = isAll ? COLORS.accent : projectColor(scope);
|
|
2767
3305
|
const label = isAll ? "All" : scope;
|
|
2768
|
-
return /* @__PURE__ */ (0,
|
|
2769
|
-
/* @__PURE__ */ (0,
|
|
2770
|
-
/* @__PURE__ */ (0,
|
|
2771
|
-
/* @__PURE__ */ (0,
|
|
3306
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { children: [
|
|
3307
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.textDim, children: "Project " }),
|
|
3308
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: "black", backgroundColor: bg, bold: true, children: ` ${label} ` }),
|
|
3309
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { color: theme.textDim, children: [
|
|
2772
3310
|
" ",
|
|
2773
|
-
/* @__PURE__ */ (0,
|
|
3311
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.primary, children: "Tab" }),
|
|
2774
3312
|
" to switch"
|
|
2775
3313
|
] })
|
|
2776
3314
|
] });
|
|
@@ -2779,396 +3317,89 @@ function scopePrefix2(scope) {
|
|
|
2779
3317
|
if (scope === "all") return "[scope:all] ";
|
|
2780
3318
|
return `[scope:${scope}] `;
|
|
2781
3319
|
}
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
const
|
|
2785
|
-
const
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
}) {
|
|
2809
|
-
const theme = useTheme();
|
|
2810
|
-
const { columns } = useTerminalSize();
|
|
2811
|
-
const working = workers.filter((w) => w.status === "working");
|
|
2812
|
-
const errored = workers.filter((w) => w.status === "error");
|
|
2813
|
-
const idleCount = workers.length - working.length - errored.length;
|
|
2814
|
-
const anyWorking = working.length > 0;
|
|
2815
|
-
const tick = useAnimationTick();
|
|
2816
|
-
const now = Date.now();
|
|
2817
|
-
if (workers.length === 0) return null;
|
|
2818
|
-
const slots = [];
|
|
2819
|
-
for (const w of working) {
|
|
2820
|
-
const projectHue = projectColor(w.name);
|
|
2821
|
-
const elapsed = w.workStartedAt ? formatElapsed(now - w.workStartedAt) : null;
|
|
2822
|
-
slots.push(
|
|
2823
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react10.default.Fragment, { children: [
|
|
2824
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ShimmerName, { name: w.name, color: projectHue, tick }),
|
|
2825
|
-
elapsed && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.textDim, children: [
|
|
2826
|
-
" ",
|
|
2827
|
-
elapsed
|
|
2828
|
-
] })
|
|
2829
|
-
] }, `w-${w.name}`)
|
|
2830
|
-
);
|
|
2831
|
-
}
|
|
2832
|
-
for (const w of errored) {
|
|
2833
|
-
slots.push(
|
|
2834
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react10.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.error, children: [
|
|
2835
|
-
"\u2717 ",
|
|
2836
|
-
w.name
|
|
2837
|
-
] }) }, `e-${w.name}`)
|
|
2838
|
-
);
|
|
2839
|
-
}
|
|
2840
|
-
if (idleCount > 0) {
|
|
2841
|
-
slots.push(
|
|
2842
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react10.default.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.textDim, children: [
|
|
2843
|
-
"\u25CB ",
|
|
2844
|
-
idleCount,
|
|
2845
|
-
" idle"
|
|
2846
|
-
] }) }, "idle")
|
|
2847
|
-
);
|
|
2848
|
-
}
|
|
2849
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { paddingX: 1, width: columns, flexShrink: 1, children: [
|
|
2850
|
-
anyWorking && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AnimationActiveSentinel, {}),
|
|
2851
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "truncate", children: [
|
|
2852
|
-
slots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react10.default.Fragment, { children: [
|
|
2853
|
-
i > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.border, children: " \u2502 " }),
|
|
2854
|
-
slot
|
|
2855
|
-
] }, i)),
|
|
2856
|
-
pendingMessages > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2857
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " " }),
|
|
2858
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.warning, children: [
|
|
2859
|
-
pendingMessages,
|
|
2860
|
-
" message",
|
|
2861
|
-
pendingMessages === 1 ? "" : "s",
|
|
2862
|
-
" queued"
|
|
2863
|
-
] })
|
|
2864
|
-
] })
|
|
2865
|
-
] })
|
|
2866
|
-
] });
|
|
2867
|
-
}
|
|
2868
|
-
function StaticRowView({ row }) {
|
|
2869
|
-
if (row.kind === "banner") {
|
|
2870
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BossBanner, { subtitle: "Orchestrator", showShortcuts: true }) });
|
|
2871
|
-
}
|
|
2872
|
-
if (row.kind === "user") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(UserMessage, { text: row.text });
|
|
2873
|
-
if (row.kind === "assistant") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AssistantRow, { item: row });
|
|
2874
|
-
if (row.kind === "tool") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolHistoryRow, { item: row });
|
|
2875
|
-
if (row.kind === "worker_event") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WorkerEventRow, { item: row });
|
|
2876
|
-
if (row.kind === "worker_error") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WorkerErrorRow, { item: row });
|
|
2877
|
-
if (row.kind === "info") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(InfoRow, { text: row.text, level: row.level ?? "info" });
|
|
2878
|
-
if (row.kind === "task_dispatch") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TaskDispatchRow, { tasks: row.tasks });
|
|
2879
|
-
if (row.kind === "update_notice") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(UpdateNoticeRow, { text: row.text });
|
|
2880
|
-
return null;
|
|
2881
|
-
}
|
|
2882
|
-
function UpdateNoticeRow({ text }) {
|
|
2883
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { marginTop: 1, flexShrink: 1, borderStyle: "round", borderColor: COLORS.accent, paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "wrap", children: [
|
|
2884
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: COLORS.accent, bold: true, children: "\u2728 " }),
|
|
2885
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: COLORS.primary, bold: true, children: text })
|
|
2886
|
-
] }) });
|
|
2887
|
-
}
|
|
2888
|
-
function TaskDispatchRow({
|
|
2889
|
-
tasks
|
|
2890
|
-
}) {
|
|
2891
|
-
const theme = useTheme();
|
|
2892
|
-
const count = tasks.length;
|
|
2893
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
2894
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
|
|
2895
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: COLORS.primary, bold: true, children: "\u23FA " }),
|
|
2896
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: theme.text, bold: true, children: [
|
|
2897
|
-
"Running ",
|
|
2898
|
-
count,
|
|
2899
|
-
" task",
|
|
2900
|
-
count === 1 ? "" : "s",
|
|
2901
|
-
":"
|
|
2902
|
-
] })
|
|
2903
|
-
] }),
|
|
2904
|
-
tasks.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
|
|
2905
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " \u2022 " }),
|
|
2906
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: projectColor(t.project), bold: true, children: t.project }),
|
|
2907
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: ": " }),
|
|
2908
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.text, children: t.title })
|
|
2909
|
-
] }, `${t.project}-${i}`))
|
|
2910
|
-
] });
|
|
2911
|
-
}
|
|
2912
|
-
var SHORTCUT_PATTERNS = [
|
|
2913
|
-
// Modifier+Key combos: Ctrl+T, Shift+Tab, Cmd+K, Ctrl+Shift+P, Ctrl+C
|
|
2914
|
-
/\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,
|
|
2915
|
-
// Bare named keys (only when surrounded by clear key context)
|
|
2916
|
-
/\b(?:Ctrl-[A-Z]|F[1-9]|F1[0-2])\b/g
|
|
2917
|
-
];
|
|
2918
|
-
function highlightShortcuts(text) {
|
|
2919
|
-
if (!text) return text;
|
|
2920
|
-
const SENTINEL = "\uE000";
|
|
2921
|
-
const masks = [];
|
|
2922
|
-
let masked = text.replace(/```[\s\S]*?```|`[^`]+`/g, (m) => {
|
|
2923
|
-
const idx = masks.push(m) - 1;
|
|
2924
|
-
return `${SENTINEL}${idx}${SENTINEL}`;
|
|
2925
|
-
});
|
|
2926
|
-
for (const re of SHORTCUT_PATTERNS) {
|
|
2927
|
-
masked = masked.replace(re, (m) => `\`${m}\``);
|
|
2928
|
-
}
|
|
2929
|
-
return masked.replace(
|
|
2930
|
-
new RegExp(`${SENTINEL}(\\d+)${SENTINEL}`, "g"),
|
|
2931
|
-
(_, i) => masks[Number(i)]
|
|
2932
|
-
);
|
|
2933
|
-
}
|
|
2934
|
-
function AssistantRow({ item }) {
|
|
2935
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2936
|
-
AssistantMessage,
|
|
2937
|
-
{
|
|
2938
|
-
text: highlightShortcuts(item.text),
|
|
2939
|
-
thinking: item.thinking,
|
|
2940
|
-
thinkingMs: item.thinkingMs
|
|
2941
|
-
}
|
|
2942
|
-
);
|
|
2943
|
-
}
|
|
2944
|
-
function ToolHistoryRow({ item }) {
|
|
2945
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2946
|
-
ToolExecution,
|
|
2947
|
-
{
|
|
2948
|
-
status: "done",
|
|
2949
|
-
name: item.name,
|
|
2950
|
-
args: item.args,
|
|
2951
|
-
result: item.result,
|
|
2952
|
-
isError: item.isError,
|
|
2953
|
-
details: item.details,
|
|
2954
|
-
formatters: bossToolFormatters
|
|
3320
|
+
function formatBossDuration(durationMs) {
|
|
3321
|
+
const total = Math.max(0, Math.floor(durationMs / 1e3));
|
|
3322
|
+
const minutes = Math.floor(total / 60);
|
|
3323
|
+
const seconds = total % 60;
|
|
3324
|
+
return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
3325
|
+
}
|
|
3326
|
+
var INK_OPTIONS = {
|
|
3327
|
+
// Match ggcoder's keyboard setup: enable kitty keyboard so Ink can decode
|
|
3328
|
+
// enhanced key events, but keep exitOnCtrlC false so our handlers receive it.
|
|
3329
|
+
kittyKeyboard: {
|
|
3330
|
+
mode: "enabled",
|
|
3331
|
+
flags: ["disambiguateEscapeCodes"]
|
|
3332
|
+
},
|
|
3333
|
+
exitOnCtrlC: false
|
|
3334
|
+
};
|
|
3335
|
+
var DISABLE_MODIFY_OTHER_KEYS = "\x1B[>4;0m";
|
|
3336
|
+
var DISABLE_FOCUS_REPORTING = "\x1B[?1004l";
|
|
3337
|
+
var SCREEN_CLEAR = DISABLE_MODIFY_OTHER_KEYS + "\x1B[2J\x1B[3J\x1B[H";
|
|
3338
|
+
var VIEWPORT_CLEAR = DISABLE_MODIFY_OTHER_KEYS + "\x1B[2J\x1B[H";
|
|
3339
|
+
function renderBossApp(opts) {
|
|
3340
|
+
const terminalHistoryPrinter = createBossTerminalHistoryPrinter({ stream: process.stdout });
|
|
3341
|
+
process.stdout.write(SCREEN_CLEAR);
|
|
3342
|
+
const onProcessExit = () => {
|
|
3343
|
+
try {
|
|
3344
|
+
process.stdout.write(DISABLE_MODIFY_OTHER_KEYS + DISABLE_FOCUS_REPORTING);
|
|
3345
|
+
} catch {
|
|
2955
3346
|
}
|
|
2956
|
-
);
|
|
2957
|
-
}
|
|
2958
|
-
function parseStatusGrade(text) {
|
|
2959
|
-
const matches = [...text.matchAll(/^\s*Status:\s*(DONE|UNVERIFIED|PARTIAL|BLOCKED|INFO)\b/gim)];
|
|
2960
|
-
const last = matches[matches.length - 1];
|
|
2961
|
-
if (!last) return null;
|
|
2962
|
-
return last[1].toUpperCase();
|
|
2963
|
-
}
|
|
2964
|
-
function parseWorkerTrailer(text) {
|
|
2965
|
-
const out = {};
|
|
2966
|
-
const grab = (label) => {
|
|
2967
|
-
const re = new RegExp(
|
|
2968
|
-
`^\\s*${label}:\\s*([\\s\\S]*?)(?=^\\s*(?:Changed|Skipped|Verified|Notes|Status):|$)`,
|
|
2969
|
-
"im"
|
|
2970
|
-
);
|
|
2971
|
-
const m = re.exec(text);
|
|
2972
|
-
if (!m) return void 0;
|
|
2973
|
-
const v = m[1].replace(/```[\s\S]*?```/g, "[code]").replace(/`([^`]+)`/g, "$1").replace(/\s+/g, " ").trim();
|
|
2974
|
-
return v.length > 0 ? v : void 0;
|
|
2975
3347
|
};
|
|
2976
|
-
|
|
2977
|
-
out.skipped = grab("Skipped");
|
|
2978
|
-
out.verified = grab("Verified");
|
|
2979
|
-
out.notes = grab("Notes");
|
|
2980
|
-
return out;
|
|
2981
|
-
}
|
|
2982
|
-
function clip(text, maxLen) {
|
|
2983
|
-
return text.length <= maxLen ? text : text.slice(0, Math.max(1, maxLen - 1)) + "\u2026";
|
|
2984
|
-
}
|
|
2985
|
-
function summarizeFinalText(text, maxLen) {
|
|
2986
|
-
if (!text) return "";
|
|
2987
|
-
const trailer = parseWorkerTrailer(text);
|
|
2988
|
-
const parts = [];
|
|
2989
|
-
if (trailer.changed) parts.push(`Changed: ${trailer.changed}`);
|
|
2990
|
-
if (trailer.verified) parts.push(`Verified: ${trailer.verified}`);
|
|
2991
|
-
if (trailer.skipped) parts.push(`Skipped: ${trailer.skipped}`);
|
|
2992
|
-
if (trailer.notes) parts.push(`Notes: ${trailer.notes}`);
|
|
2993
|
-
if (parts.length > 0) return clip(parts.join(" \xB7 "), maxLen);
|
|
2994
|
-
const beforeSummary = text.split(/^Changed:|^Skipped:|^Verified:|^Notes:|^Status:/im)[0];
|
|
2995
|
-
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();
|
|
2996
|
-
if (!stripped) return "";
|
|
2997
|
-
const firstSentence = stripped.match(/^[^.!?\n]+[.!?]/);
|
|
2998
|
-
return clip(firstSentence ? firstSentence[0] : stripped, maxLen);
|
|
2999
|
-
}
|
|
3000
|
-
function statusGradeColor(grade, theme) {
|
|
3001
|
-
switch (grade) {
|
|
3002
|
-
case "DONE":
|
|
3003
|
-
return theme.success;
|
|
3004
|
-
case "UNVERIFIED":
|
|
3005
|
-
case "PARTIAL":
|
|
3006
|
-
return theme.warning;
|
|
3007
|
-
case "BLOCKED":
|
|
3008
|
-
return theme.error;
|
|
3009
|
-
case "INFO":
|
|
3010
|
-
return theme.textDim;
|
|
3011
|
-
default:
|
|
3012
|
-
return theme.textDim;
|
|
3013
|
-
}
|
|
3014
|
-
}
|
|
3015
|
-
function WorkerEventRow({ item }) {
|
|
3016
|
-
const theme = useTheme();
|
|
3017
|
-
const { columns } = useTerminalSize();
|
|
3018
|
-
const failedCount = item.toolsUsed.filter((t) => !t.ok).length;
|
|
3019
|
-
const total = item.toolsUsed.length;
|
|
3020
|
-
const grade = parseStatusGrade(item.finalText);
|
|
3021
|
-
const loaderStatus = grade === "BLOCKED" || failedCount > 0 ? "error" : grade === "UNVERIFIED" || grade === "PARTIAL" ? "queued" : "done";
|
|
3022
|
-
const headerColor = loaderStatus === "error" ? theme.toolError : projectColor(item.project);
|
|
3023
|
-
const toolSummary = total === 0 ? "no tools" : failedCount > 0 ? `${total} tools (${failedCount} failed)` : `${total} tool${total === 1 ? "" : "s"}`;
|
|
3024
|
-
const fieldMaxLen = Math.max(20, columns - 14);
|
|
3025
|
-
const trailer = parseWorkerTrailer(item.finalText);
|
|
3026
|
-
const hasTrailer = !!(trailer.changed || trailer.skipped || trailer.verified || trailer.notes);
|
|
3027
|
-
const fallbackSummary = hasTrailer ? "" : summarizeFinalText(item.finalText, fieldMaxLen);
|
|
3028
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
|
|
3029
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "row", children: [
|
|
3030
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolUseLoader, { status: loaderStatus }),
|
|
3031
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "wrap", children: [
|
|
3032
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: headerColor, bold: true, children: item.project }),
|
|
3033
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.text, children: ` turn ${item.turnIndex}` }),
|
|
3034
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: ` \xB7 ${toolSummary}` }),
|
|
3035
|
-
grade && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
3036
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " \xB7 " }),
|
|
3037
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: statusGradeColor(grade, theme), bold: true, children: grade })
|
|
3038
|
-
] })
|
|
3039
|
-
] }) })
|
|
3040
|
-
] }),
|
|
3041
|
-
hasTrailer ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
3042
|
-
trailer.changed && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TrailerLine, { label: "Changed", value: trailer.changed, maxLen: fieldMaxLen }),
|
|
3043
|
-
trailer.verified && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3044
|
-
TrailerLine,
|
|
3045
|
-
{
|
|
3046
|
-
label: "Verified",
|
|
3047
|
-
value: trailer.verified,
|
|
3048
|
-
maxLen: fieldMaxLen,
|
|
3049
|
-
labelColor: theme.success
|
|
3050
|
-
}
|
|
3051
|
-
),
|
|
3052
|
-
trailer.skipped && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3053
|
-
TrailerLine,
|
|
3054
|
-
{
|
|
3055
|
-
label: "Skipped",
|
|
3056
|
-
value: trailer.skipped,
|
|
3057
|
-
maxLen: fieldMaxLen,
|
|
3058
|
-
labelColor: theme.warning
|
|
3059
|
-
}
|
|
3060
|
-
),
|
|
3061
|
-
trailer.notes && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TrailerLine, { label: "Notes", value: trailer.notes, maxLen: fieldMaxLen })
|
|
3062
|
-
] }) : fallbackSummary && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, wrap: "truncate", children: fallbackSummary }) })
|
|
3063
|
-
] });
|
|
3064
|
-
}
|
|
3065
|
-
function TrailerLine({
|
|
3066
|
-
label,
|
|
3067
|
-
value,
|
|
3068
|
-
maxLen,
|
|
3069
|
-
labelColor
|
|
3070
|
-
}) {
|
|
3071
|
-
const theme = useTheme();
|
|
3072
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "truncate", children: [
|
|
3073
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { color: labelColor ?? theme.textDim, bold: true, children: [
|
|
3074
|
-
label,
|
|
3075
|
-
":"
|
|
3076
|
-
] }),
|
|
3077
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.text, children: ` ${clip(value, maxLen - label.length - 2)}` })
|
|
3078
|
-
] }) });
|
|
3079
|
-
}
|
|
3080
|
-
function WorkerErrorRow({ item }) {
|
|
3081
|
-
const theme = useTheme();
|
|
3082
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
|
|
3083
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "row", children: [
|
|
3084
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolUseLoader, { status: "error" }),
|
|
3085
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { wrap: "wrap", children: [
|
|
3086
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.toolError, bold: true, children: item.project }),
|
|
3087
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.textDim, children: " worker error" })
|
|
3088
|
-
] }) })
|
|
3089
|
-
] }),
|
|
3090
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: theme.error, wrap: "wrap", children: item.message }) })
|
|
3091
|
-
] });
|
|
3092
|
-
}
|
|
3093
|
-
function InfoRow({
|
|
3094
|
-
text,
|
|
3095
|
-
level
|
|
3096
|
-
}) {
|
|
3097
|
-
if (level === "info") return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AssistantMessage, { text });
|
|
3098
|
-
const theme = useTheme();
|
|
3099
|
-
const color = level === "error" ? theme.error : theme.warning;
|
|
3100
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { marginTop: 1, flexDirection: "row", children: [
|
|
3101
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolUseLoader, { status: level === "error" ? "error" : "queued" }),
|
|
3102
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color, wrap: "wrap", children: text }) })
|
|
3103
|
-
] });
|
|
3104
|
-
}
|
|
3105
|
-
function StreamingTurnView({
|
|
3106
|
-
turn,
|
|
3107
|
-
isRunning
|
|
3108
|
-
}) {
|
|
3109
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
3110
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3111
|
-
StreamingArea,
|
|
3112
|
-
{
|
|
3113
|
-
isRunning,
|
|
3114
|
-
streamingText: turn.text,
|
|
3115
|
-
streamingThinking: turn.thinking,
|
|
3116
|
-
thinkingMs: turn.thinkingMs
|
|
3117
|
-
}
|
|
3118
|
-
),
|
|
3119
|
-
turn.tools.map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StreamingToolRow, { tool: t }, t.toolCallId))
|
|
3120
|
-
] });
|
|
3121
|
-
}
|
|
3122
|
-
function StreamingToolRow({ tool }) {
|
|
3123
|
-
if (tool.status === "running") {
|
|
3124
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3125
|
-
ToolExecution,
|
|
3126
|
-
{
|
|
3127
|
-
status: "running",
|
|
3128
|
-
name: tool.name,
|
|
3129
|
-
args: tool.args,
|
|
3130
|
-
formatters: bossToolFormatters
|
|
3131
|
-
}
|
|
3132
|
-
);
|
|
3133
|
-
}
|
|
3134
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3135
|
-
ToolExecution,
|
|
3136
|
-
{
|
|
3137
|
-
status: "done",
|
|
3138
|
-
name: tool.name,
|
|
3139
|
-
args: tool.args,
|
|
3140
|
-
result: tool.result ?? "",
|
|
3141
|
-
isError: tool.status === "error",
|
|
3142
|
-
details: tool.details,
|
|
3143
|
-
formatters: bossToolFormatters
|
|
3144
|
-
}
|
|
3145
|
-
);
|
|
3146
|
-
}
|
|
3147
|
-
function renderBossApp(opts) {
|
|
3348
|
+
process.on("exit", onProcessExit);
|
|
3148
3349
|
const ref = { instance: null };
|
|
3149
|
-
const resetUI = () => {
|
|
3350
|
+
const resetUI = (reason = "viewport") => {
|
|
3150
3351
|
const old = ref.instance;
|
|
3151
3352
|
if (!old) return;
|
|
3152
|
-
process.stdout.write("\x1B[2J\x1B[H");
|
|
3153
3353
|
old.unmount();
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3354
|
+
if (reason === "resize-redraw") {
|
|
3355
|
+
terminalHistoryPrinter.resetPrinted();
|
|
3356
|
+
process.stdout.write(SCREEN_CLEAR);
|
|
3357
|
+
const snapshot = getBossState();
|
|
3358
|
+
if (snapshot.history.length > 0) {
|
|
3359
|
+
terminalHistoryPrinter.print(snapshot.history, {
|
|
3360
|
+
theme: loadTheme("dark"),
|
|
3361
|
+
columns: Math.max(40, process.stdout.columns ?? 80),
|
|
3362
|
+
version: VERSION,
|
|
3363
|
+
model: snapshot.bossModel,
|
|
3364
|
+
provider: snapshot.bossProvider,
|
|
3365
|
+
cwd: process.cwd()
|
|
3366
|
+
});
|
|
3367
|
+
}
|
|
3368
|
+
} else if (reason === "session-clear") {
|
|
3369
|
+
terminalHistoryPrinter.clear();
|
|
3370
|
+
process.stdout.write(SCREEN_CLEAR);
|
|
3371
|
+
} else {
|
|
3372
|
+
process.stdout.write(VIEWPORT_CLEAR);
|
|
3373
|
+
}
|
|
3374
|
+
ref.instance = render_default(
|
|
3375
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3376
|
+
BossApp,
|
|
3377
|
+
{
|
|
3378
|
+
boss: opts.boss,
|
|
3379
|
+
resetUI,
|
|
3380
|
+
terminalHistoryPrinter
|
|
3381
|
+
}
|
|
3382
|
+
),
|
|
3383
|
+
INK_OPTIONS
|
|
3384
|
+
);
|
|
3157
3385
|
};
|
|
3158
|
-
const instance = render_default(
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
// the very first Ctrl+C and InputArea's onAbort never runs.
|
|
3163
|
-
exitOnCtrlC: false
|
|
3164
|
-
});
|
|
3386
|
+
const instance = render_default(
|
|
3387
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(BossApp, { boss: opts.boss, resetUI, terminalHistoryPrinter }),
|
|
3388
|
+
INK_OPTIONS
|
|
3389
|
+
);
|
|
3165
3390
|
ref.instance = instance;
|
|
3166
3391
|
let resizeTimer = null;
|
|
3392
|
+
let resizeListenerEnabled = false;
|
|
3393
|
+
const enableResizeListener = setTimeout(() => {
|
|
3394
|
+
resizeListenerEnabled = true;
|
|
3395
|
+
}, 1e3);
|
|
3167
3396
|
const onTerminalResize = () => {
|
|
3397
|
+
if (!resizeListenerEnabled) return;
|
|
3168
3398
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
3169
3399
|
resizeTimer = setTimeout(() => {
|
|
3170
3400
|
resizeTimer = null;
|
|
3171
|
-
|
|
3401
|
+
if (getBossState().phase === "working") return;
|
|
3402
|
+
resetUI("resize-redraw");
|
|
3172
3403
|
}, 250);
|
|
3173
3404
|
};
|
|
3174
3405
|
process.stdout.on("resize", onTerminalResize);
|
|
@@ -3183,21 +3414,30 @@ function renderBossApp(opts) {
|
|
|
3183
3414
|
const current = ref.instance;
|
|
3184
3415
|
if (!current) {
|
|
3185
3416
|
process.stdout.off("resize", onTerminalResize);
|
|
3417
|
+
process.off("exit", onProcessExit);
|
|
3418
|
+
clearTimeout(enableResizeListener);
|
|
3186
3419
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
3420
|
+
onProcessExit();
|
|
3187
3421
|
return;
|
|
3188
3422
|
}
|
|
3189
3423
|
await current.waitUntilExit();
|
|
3190
3424
|
if (ref.instance === current) {
|
|
3191
3425
|
ref.instance = null;
|
|
3192
3426
|
process.stdout.off("resize", onTerminalResize);
|
|
3427
|
+
process.off("exit", onProcessExit);
|
|
3428
|
+
clearTimeout(enableResizeListener);
|
|
3193
3429
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
3430
|
+
onProcessExit();
|
|
3194
3431
|
return;
|
|
3195
3432
|
}
|
|
3196
3433
|
}
|
|
3197
3434
|
},
|
|
3198
3435
|
unmount: () => {
|
|
3199
3436
|
process.stdout.off("resize", onTerminalResize);
|
|
3437
|
+
process.off("exit", onProcessExit);
|
|
3438
|
+
clearTimeout(enableResizeListener);
|
|
3200
3439
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
3440
|
+
onProcessExit();
|
|
3201
3441
|
ref.instance?.unmount();
|
|
3202
3442
|
}
|
|
3203
3443
|
};
|
|
@@ -3205,8 +3445,8 @@ function renderBossApp(opts) {
|
|
|
3205
3445
|
|
|
3206
3446
|
// src/splash.tsx
|
|
3207
3447
|
init_esm_shims();
|
|
3208
|
-
var
|
|
3209
|
-
var
|
|
3448
|
+
var import_react15 = __toESM(require_react(), 1);
|
|
3449
|
+
var import_jsx_runtime15 = __toESM(require_jsx_runtime(), 1);
|
|
3210
3450
|
var SPLASH_LINES = [
|
|
3211
3451
|
" \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 ",
|
|
3212
3452
|
" \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 ",
|
|
@@ -3224,33 +3464,33 @@ function colorForLine(lineIdx, totalLines, offset) {
|
|
|
3224
3464
|
return GRADIENT[idx];
|
|
3225
3465
|
}
|
|
3226
3466
|
function SplashLogo({ offset }) {
|
|
3227
|
-
return /* @__PURE__ */ (0,
|
|
3467
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { flexDirection: "column", children: SPLASH_LINES.map((line, i) => {
|
|
3228
3468
|
const hue = colorForLine(i, SPLASH_LINES.length, offset);
|
|
3229
3469
|
const segments = [];
|
|
3230
3470
|
let buf = "";
|
|
3231
3471
|
let bufDim = false;
|
|
3232
3472
|
for (const ch of line) {
|
|
3233
|
-
const
|
|
3473
|
+
const dim2 = ch === "\u2591";
|
|
3234
3474
|
if (segments.length === 0 && buf.length === 0) {
|
|
3235
3475
|
buf = ch;
|
|
3236
|
-
bufDim =
|
|
3476
|
+
bufDim = dim2;
|
|
3237
3477
|
continue;
|
|
3238
3478
|
}
|
|
3239
|
-
if (
|
|
3479
|
+
if (dim2 === bufDim) {
|
|
3240
3480
|
buf += ch;
|
|
3241
3481
|
} else {
|
|
3242
3482
|
segments.push({ text: buf, dim: bufDim });
|
|
3243
3483
|
buf = ch;
|
|
3244
|
-
bufDim =
|
|
3484
|
+
bufDim = dim2;
|
|
3245
3485
|
}
|
|
3246
3486
|
}
|
|
3247
3487
|
if (buf) segments.push({ text: buf, dim: bufDim });
|
|
3248
|
-
return /* @__PURE__ */ (0,
|
|
3488
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: segments.map((seg, j) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: hue, dimColor: seg.dim, children: seg.text }, j)) }, i);
|
|
3249
3489
|
}) });
|
|
3250
3490
|
}
|
|
3251
3491
|
function SplashScreen({ caption }) {
|
|
3252
|
-
const [offset, setOffset] = (0,
|
|
3253
|
-
(0,
|
|
3492
|
+
const [offset, setOffset] = (0, import_react15.useState)(0);
|
|
3493
|
+
(0, import_react15.useEffect)(() => {
|
|
3254
3494
|
const timer = setInterval(() => {
|
|
3255
3495
|
setOffset((o) => o + 1);
|
|
3256
3496
|
}, 120);
|
|
@@ -3258,11 +3498,11 @@ function SplashScreen({ caption }) {
|
|
|
3258
3498
|
clearInterval(timer);
|
|
3259
3499
|
};
|
|
3260
3500
|
}, []);
|
|
3261
|
-
const [size, setSize] = (0,
|
|
3501
|
+
const [size, setSize] = (0, import_react15.useState)(() => ({
|
|
3262
3502
|
columns: process.stdout.columns ?? 80,
|
|
3263
3503
|
rows: process.stdout.rows ?? 24
|
|
3264
3504
|
}));
|
|
3265
|
-
(0,
|
|
3505
|
+
(0, import_react15.useEffect)(() => {
|
|
3266
3506
|
const handler = () => setSize({
|
|
3267
3507
|
columns: process.stdout.columns ?? 80,
|
|
3268
3508
|
rows: process.stdout.rows ?? 24
|
|
@@ -3274,27 +3514,27 @@ function SplashScreen({ caption }) {
|
|
|
3274
3514
|
}, []);
|
|
3275
3515
|
const SPLASH_BLOCK_HEIGHT = SPLASH_LINES.length + 3;
|
|
3276
3516
|
const verticalPad = Math.max(0, Math.floor((size.rows - SPLASH_BLOCK_HEIGHT) / 2));
|
|
3277
|
-
return /* @__PURE__ */ (0,
|
|
3278
|
-
/* @__PURE__ */ (0,
|
|
3279
|
-
/* @__PURE__ */ (0,
|
|
3280
|
-
/* @__PURE__ */ (0,
|
|
3281
|
-
/* @__PURE__ */ (0,
|
|
3282
|
-
/* @__PURE__ */ (0,
|
|
3283
|
-
/* @__PURE__ */ (0,
|
|
3517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { flexDirection: "column", width: size.columns, height: size.rows, alignItems: "center", children: [
|
|
3518
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { height: verticalPad, flexShrink: 0 }),
|
|
3519
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { flexDirection: "column", alignItems: "flex-start", flexShrink: 0, children: [
|
|
3520
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SplashLogo, { offset }),
|
|
3521
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { width: SPLASH_WIDTH, marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text, { children: [
|
|
3522
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: COLORS.text, bold: true, children: BRAND }),
|
|
3523
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text, { color: COLORS.textDim, children: [
|
|
3284
3524
|
" v",
|
|
3285
3525
|
VERSION
|
|
3286
3526
|
] }),
|
|
3287
|
-
/* @__PURE__ */ (0,
|
|
3288
|
-
/* @__PURE__ */ (0,
|
|
3527
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: COLORS.textDim, children: " \xB7 By " }),
|
|
3528
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: COLORS.text, bold: true, children: AUTHOR })
|
|
3289
3529
|
] }) }),
|
|
3290
|
-
/* @__PURE__ */ (0,
|
|
3530
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { width: SPLASH_WIDTH, justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: COLORS.textDim, children: caption ?? "Spinning up the orchestrator\u2026" }) })
|
|
3291
3531
|
] })
|
|
3292
3532
|
] });
|
|
3293
3533
|
}
|
|
3294
3534
|
function showSplash(opts) {
|
|
3295
3535
|
const start = Date.now();
|
|
3296
3536
|
void playSplashAudio();
|
|
3297
|
-
const instance = render_default(/* @__PURE__ */ (0,
|
|
3537
|
+
const instance = render_default(/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SplashScreen, { caption: opts.caption }));
|
|
3298
3538
|
const audioDurationMs = getSplashAudioDurationMs();
|
|
3299
3539
|
const defaultMinMs = audioDurationMs + 200;
|
|
3300
3540
|
return {
|
|
@@ -3353,12 +3593,12 @@ function parseArgs(argv) {
|
|
|
3353
3593
|
return args;
|
|
3354
3594
|
}
|
|
3355
3595
|
function printHelpAndExit() {
|
|
3356
|
-
const c = (
|
|
3596
|
+
const c = (color2, text) => source_default.hex(color2)(text);
|
|
3357
3597
|
process.stdout.write(
|
|
3358
3598
|
"\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(
|
|
3359
3599
|
COLORS.textDim,
|
|
3360
3600
|
" start orchestrator using linked projects\n"
|
|
3361
|
-
) + " " + c(COLORS.accent, "ggboss link") + c(COLORS.textDim, " pick which projects to link (interactive)\n") + " " + c(COLORS.accent, "ggboss telegram") + c(COLORS.textDim, " configure Telegram bot integration\n") + " " + c(COLORS.accent, "ggboss serve") + c(COLORS.textDim, " run the boss over Telegram (no TUI)\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-
|
|
3601
|
+
) + " " + c(COLORS.accent, "ggboss link") + c(COLORS.textDim, " pick which projects to link (interactive)\n") + " " + c(COLORS.accent, "ggboss telegram") + c(COLORS.textDim, " configure Telegram bot integration\n") + " " + c(COLORS.accent, "ggboss serve") + c(COLORS.textDim, " run the boss over Telegram (no TUI)\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-8)\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")
|
|
3362
3602
|
);
|
|
3363
3603
|
process.exit(0);
|
|
3364
3604
|
}
|
|
@@ -3393,10 +3633,12 @@ async function runServeSubcommand(argv) {
|
|
|
3393
3633
|
process.exit(1);
|
|
3394
3634
|
}
|
|
3395
3635
|
const settings = await loadSettings();
|
|
3396
|
-
const bossProvider =
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3636
|
+
const { bossProvider, bossModel, workerProvider, workerModel } = await resolveBossAuth({
|
|
3637
|
+
bossProvider: settings.bossProvider ?? "anthropic",
|
|
3638
|
+
bossModel: cliBossModel ?? settings.bossModel ?? "claude-opus-4-8",
|
|
3639
|
+
workerProvider: settings.workerProvider ?? "anthropic",
|
|
3640
|
+
workerModel: cliWorkerModel ?? settings.workerModel ?? "claude-sonnet-4-6"
|
|
3641
|
+
});
|
|
3400
3642
|
await runBossServeMode({
|
|
3401
3643
|
bossProvider,
|
|
3402
3644
|
bossModel,
|
|
@@ -3406,6 +3648,45 @@ async function runServeSubcommand(argv) {
|
|
|
3406
3648
|
telegram: { botToken, userId }
|
|
3407
3649
|
});
|
|
3408
3650
|
}
|
|
3651
|
+
var ALL_PROVIDERS = [
|
|
3652
|
+
"anthropic",
|
|
3653
|
+
"openai",
|
|
3654
|
+
"xiaomi",
|
|
3655
|
+
"gemini",
|
|
3656
|
+
"glm",
|
|
3657
|
+
"moonshot",
|
|
3658
|
+
"minimax",
|
|
3659
|
+
"deepseek",
|
|
3660
|
+
"openrouter"
|
|
3661
|
+
];
|
|
3662
|
+
function bossDefaultModel(provider) {
|
|
3663
|
+
return provider === "anthropic" ? "claude-opus-4-8" : getDefaultModel(provider).id;
|
|
3664
|
+
}
|
|
3665
|
+
async function resolveBossAuth(input) {
|
|
3666
|
+
const auth = new AuthStorage();
|
|
3667
|
+
await auth.load();
|
|
3668
|
+
const stored = await auth.listProviders();
|
|
3669
|
+
const loggedIn = ALL_PROVIDERS.filter((p) => stored.includes(p));
|
|
3670
|
+
if (loggedIn.length === 0) {
|
|
3671
|
+
throw new Error('Not logged in to any provider. Run "ggcoder login" to authenticate.');
|
|
3672
|
+
}
|
|
3673
|
+
const fallback = loggedIn[0];
|
|
3674
|
+
const resolve = (preferredProvider, preferredModel, defaultFor) => {
|
|
3675
|
+
const provider = loggedIn.includes(preferredProvider) ? preferredProvider : fallback;
|
|
3676
|
+
const modelFits = getModel(preferredModel)?.provider === provider;
|
|
3677
|
+
return { provider, model: modelFits ? preferredModel : defaultFor(provider) };
|
|
3678
|
+
};
|
|
3679
|
+
const boss = resolve(input.bossProvider, input.bossModel, bossDefaultModel);
|
|
3680
|
+
const worker = resolve(input.workerProvider, input.workerModel, (p) => getDefaultModel(p).id);
|
|
3681
|
+
const fellBack = boss.provider !== input.bossProvider || worker.provider !== input.workerProvider;
|
|
3682
|
+
return {
|
|
3683
|
+
bossProvider: boss.provider,
|
|
3684
|
+
bossModel: boss.model,
|
|
3685
|
+
workerProvider: worker.provider,
|
|
3686
|
+
workerModel: worker.model,
|
|
3687
|
+
fellBack
|
|
3688
|
+
};
|
|
3689
|
+
}
|
|
3409
3690
|
async function runOrchestrator(args) {
|
|
3410
3691
|
if (args.projects.length === 0) {
|
|
3411
3692
|
const links = await loadLinks();
|
|
@@ -3422,10 +3703,30 @@ async function runOrchestrator(args) {
|
|
|
3422
3703
|
caption: `Spinning up ${args.projects.length} worker${args.projects.length === 1 ? "" : "s"}\u2026`
|
|
3423
3704
|
});
|
|
3424
3705
|
const settings = await loadSettings();
|
|
3425
|
-
const
|
|
3426
|
-
const
|
|
3427
|
-
const
|
|
3428
|
-
const
|
|
3706
|
+
const preferredBossProvider = args.bossProvider ?? settings.bossProvider ?? "anthropic";
|
|
3707
|
+
const preferredBossModel = args.bossModel ?? settings.bossModel ?? "claude-opus-4-8";
|
|
3708
|
+
const preferredWorkerProvider = args.workerProvider ?? settings.workerProvider ?? "anthropic";
|
|
3709
|
+
const preferredWorkerModel = args.workerModel ?? settings.workerModel ?? "claude-sonnet-4-6";
|
|
3710
|
+
const {
|
|
3711
|
+
bossProvider: finalBossProvider,
|
|
3712
|
+
bossModel: finalBossModel,
|
|
3713
|
+
workerProvider: finalWorkerProvider,
|
|
3714
|
+
workerModel: finalWorkerModel,
|
|
3715
|
+
fellBack
|
|
3716
|
+
} = await resolveBossAuth({
|
|
3717
|
+
bossProvider: preferredBossProvider,
|
|
3718
|
+
bossModel: preferredBossModel,
|
|
3719
|
+
workerProvider: preferredWorkerProvider,
|
|
3720
|
+
workerModel: preferredWorkerModel
|
|
3721
|
+
});
|
|
3722
|
+
if (fellBack) {
|
|
3723
|
+
log("INFO", "cli", "provider fallback", {
|
|
3724
|
+
preferredBoss: preferredBossProvider,
|
|
3725
|
+
boss: finalBossProvider,
|
|
3726
|
+
preferredWorker: preferredWorkerProvider,
|
|
3727
|
+
worker: finalWorkerProvider
|
|
3728
|
+
});
|
|
3729
|
+
}
|
|
3429
3730
|
initLogger({
|
|
3430
3731
|
version: VERSION,
|
|
3431
3732
|
bossProvider: finalBossProvider,
|