@nomad-e/bluma-cli 0.1.32 → 0.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +510 -100
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -17,7 +17,7 @@ __export(async_command_exports, {
|
|
|
17
17
|
runCommandAsync: () => runCommandAsync,
|
|
18
18
|
sendCommandInput: () => sendCommandInput
|
|
19
19
|
});
|
|
20
|
-
import
|
|
20
|
+
import os3 from "os";
|
|
21
21
|
import { spawn } from "child_process";
|
|
22
22
|
import { v4 as uuidv42 } from "uuid";
|
|
23
23
|
function cleanupOldCommands() {
|
|
@@ -63,7 +63,7 @@ async function runCommandAsync(args) {
|
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
const commandId = uuidv42().substring(0, 8);
|
|
66
|
-
const platform =
|
|
66
|
+
const platform = os3.platform();
|
|
67
67
|
let shellCmd;
|
|
68
68
|
let shellArgs;
|
|
69
69
|
if (platform === "win32") {
|
|
@@ -331,7 +331,9 @@ var init_async_command = __esm({
|
|
|
331
331
|
import React12 from "react";
|
|
332
332
|
import { render } from "ink";
|
|
333
333
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
334
|
-
import
|
|
334
|
+
import fs16 from "fs";
|
|
335
|
+
import path20 from "path";
|
|
336
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
335
337
|
import { v4 as uuidv46 } from "uuid";
|
|
336
338
|
|
|
337
339
|
// src/app/ui/App.tsx
|
|
@@ -339,8 +341,9 @@ import { useState as useState6, useEffect as useEffect7, useRef as useRef5, useC
|
|
|
339
341
|
import { Box as Box20, Text as Text19, Static } from "ink";
|
|
340
342
|
|
|
341
343
|
// src/app/ui/layout.tsx
|
|
342
|
-
import { Box, Text } from "ink";
|
|
344
|
+
import { Box, Text, useStdout } from "ink";
|
|
343
345
|
import { memo } from "react";
|
|
346
|
+
import os from "os";
|
|
344
347
|
|
|
345
348
|
// src/app/ui/theme/blumaTerminal.ts
|
|
346
349
|
var BLUMA_TERMINAL = {
|
|
@@ -375,24 +378,106 @@ var BLUMA_TERMINAL = {
|
|
|
375
378
|
|
|
376
379
|
// src/app/ui/layout.tsx
|
|
377
380
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
381
|
+
function buildTopBorder(cols, leftW, rightW, titleInLeft) {
|
|
382
|
+
const t = titleInLeft.length <= leftW ? titleInLeft : titleInLeft.slice(0, leftW);
|
|
383
|
+
const leftFill = t + "\u2500".repeat(Math.max(0, leftW - t.length));
|
|
384
|
+
const rightFill = "\u2500".repeat(rightW);
|
|
385
|
+
return "\u250C" + leftFill + "\u252C" + rightFill + "\u2510";
|
|
386
|
+
}
|
|
387
|
+
function buildBottomBorder(leftW, rightW) {
|
|
388
|
+
return "\u2514" + "\u2500".repeat(leftW) + "\u2534" + "\u2500".repeat(rightW) + "\u2518";
|
|
389
|
+
}
|
|
378
390
|
var HeaderComponent = ({
|
|
379
391
|
sessionId,
|
|
380
|
-
workdir
|
|
392
|
+
workdir,
|
|
393
|
+
cliVersion = "?",
|
|
394
|
+
recentActivitySummary
|
|
381
395
|
}) => {
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
396
|
+
const { stdout } = useStdout();
|
|
397
|
+
const cols = Math.max(52, stdout?.columns ?? 80);
|
|
398
|
+
let username = "there";
|
|
399
|
+
try {
|
|
400
|
+
username = os.userInfo().username || username;
|
|
401
|
+
} catch {
|
|
402
|
+
}
|
|
403
|
+
const inner = cols - 3;
|
|
404
|
+
const leftW = Math.max(24, Math.floor(inner * 0.48));
|
|
405
|
+
const rightW = Math.max(22, inner - leftW);
|
|
406
|
+
const borderTitle = ` BluMa v${cliVersion} `;
|
|
407
|
+
const topLine = buildTopBorder(cols, leftW, rightW, borderTitle);
|
|
408
|
+
const bottomLine = buildBottomBorder(leftW, rightW);
|
|
409
|
+
const M = BLUMA_TERMINAL.brandMagenta;
|
|
410
|
+
const B = BLUMA_TERMINAL.panelBorder;
|
|
411
|
+
const ruleW = Math.max(6, rightW - 2);
|
|
412
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, width: cols, children: [
|
|
413
|
+
/* @__PURE__ */ jsx(Text, { color: B, children: topLine }),
|
|
414
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", width: cols, children: [
|
|
415
|
+
/* @__PURE__ */ jsx(Text, { color: B, children: "\u2502" }),
|
|
416
|
+
/* @__PURE__ */ jsxs(
|
|
417
|
+
Box,
|
|
418
|
+
{
|
|
419
|
+
width: leftW,
|
|
420
|
+
flexDirection: "column",
|
|
421
|
+
alignItems: "center",
|
|
422
|
+
paddingX: 1,
|
|
423
|
+
children: [
|
|
424
|
+
/* @__PURE__ */ jsxs(Text, { wrap: "wrap", children: [
|
|
425
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: M, children: "BluMa" }),
|
|
426
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 " }),
|
|
427
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: B, children: "Base Language Unit" }),
|
|
428
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
429
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: M, children: "Model Agent" })
|
|
430
|
+
] }),
|
|
431
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, flexDirection: "column", alignItems: "center", children: /* @__PURE__ */ jsxs(Text, { children: [
|
|
432
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Welcome back " }),
|
|
433
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "white", children: username }),
|
|
434
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "!" })
|
|
435
|
+
] }) }),
|
|
436
|
+
/* @__PURE__ */ jsxs(Box, { marginY: 1, flexDirection: "column", alignItems: "center", children: [
|
|
437
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(Math.min(13, Math.max(7, leftW - 6))) }),
|
|
438
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: M, children: " \u27E8 \u03BB \u27E9 " }),
|
|
439
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(Math.min(13, Math.max(7, leftW - 6))) })
|
|
440
|
+
] }),
|
|
441
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignSelf: "flex-start", width: leftW - 2, children: [
|
|
442
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, wrap: "wrap", children: [
|
|
443
|
+
/* @__PURE__ */ jsx(Text, { color: "white", children: "auto" }),
|
|
444
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 FactorRouter \xB7 session " }),
|
|
445
|
+
/* @__PURE__ */ jsx(Text, { color: B, children: sessionId.slice(0, 8) })
|
|
446
|
+
] }),
|
|
447
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: workdir })
|
|
448
|
+
] })
|
|
449
|
+
]
|
|
450
|
+
}
|
|
451
|
+
),
|
|
452
|
+
/* @__PURE__ */ jsx(Text, { color: B, children: "\u2502" }),
|
|
453
|
+
/* @__PURE__ */ jsxs(Box, { width: rightW, flexDirection: "column", paddingX: 1, children: [
|
|
454
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: M, children: "Tips for getting started" }),
|
|
455
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, wrap: "wrap", children: [
|
|
456
|
+
"Run ",
|
|
457
|
+
/* @__PURE__ */ jsx(Text, { color: "white", children: "/init" }),
|
|
458
|
+
" to scaffold project context (BluMa.md). Use",
|
|
459
|
+
" ",
|
|
460
|
+
/* @__PURE__ */ jsx(Text, { color: "white", children: "/help" }),
|
|
461
|
+
", ",
|
|
462
|
+
/* @__PURE__ */ jsx(Text, { color: "white", children: "/img" }),
|
|
463
|
+
", ",
|
|
464
|
+
/* @__PURE__ */ jsx(Text, { color: "white", children: "!" }),
|
|
465
|
+
" ",
|
|
466
|
+
"as needed."
|
|
467
|
+
] }),
|
|
468
|
+
/* @__PURE__ */ jsx(Box, { marginY: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(ruleW) }) }),
|
|
469
|
+
/* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: M, children: "Recent activity" }) }),
|
|
470
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: recentActivitySummary?.trim() ? recentActivitySummary.trim() : "No recent activity." })
|
|
471
|
+
] }),
|
|
472
|
+
/* @__PURE__ */ jsx(Text, { color: B, children: "\u2502" })
|
|
390
473
|
] }),
|
|
391
|
-
/* @__PURE__ */
|
|
392
|
-
|
|
393
|
-
/* @__PURE__ */ jsx(Text, {
|
|
394
|
-
/* @__PURE__ */ jsx(Text, {
|
|
395
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children:
|
|
474
|
+
/* @__PURE__ */ jsx(Text, { color: B, children: bottomLine }),
|
|
475
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
476
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 " }),
|
|
477
|
+
/* @__PURE__ */ jsx(Text, { color: B, bold: true, children: "NomadEngenuity" }),
|
|
478
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 Factor stack \xB7 " }),
|
|
479
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: M, children: "/help" }),
|
|
480
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " for shortcuts" })
|
|
396
481
|
] })
|
|
397
482
|
] });
|
|
398
483
|
};
|
|
@@ -441,7 +526,7 @@ var TaskStatusBarComponent = ({
|
|
|
441
526
|
var TaskStatusBar = memo(TaskStatusBarComponent);
|
|
442
527
|
|
|
443
528
|
// src/app/ui/components/InputPrompt.tsx
|
|
444
|
-
import { Box as Box2, Text as Text2, useStdout, useInput as useInput2 } from "ink";
|
|
529
|
+
import { Box as Box2, Text as Text2, useStdout as useStdout2, useInput as useInput2 } from "ink";
|
|
445
530
|
|
|
446
531
|
// src/app/ui/utils/useSimpleInputBuffer.ts
|
|
447
532
|
import { useReducer, useRef, useCallback, useEffect } from "react";
|
|
@@ -742,12 +827,68 @@ import { EventEmitter as EventEmitter2 } from "events";
|
|
|
742
827
|
|
|
743
828
|
// src/app/ui/utils/slashRegistry.ts
|
|
744
829
|
var getSlashCommands = () => [
|
|
745
|
-
{
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
830
|
+
{
|
|
831
|
+
name: "/help",
|
|
832
|
+
description: "list all slash commands (grouped)",
|
|
833
|
+
category: "help"
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
name: "/clear",
|
|
837
|
+
description: "clear chat below the welcome panel (welcome + session file unchanged)",
|
|
838
|
+
category: "session"
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
name: "/img",
|
|
842
|
+
description: "send local image(s) to the model: /img ./shot.png [your question]",
|
|
843
|
+
category: "agent"
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
name: "/image",
|
|
847
|
+
description: "alias of /img",
|
|
848
|
+
category: "agent"
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
name: "/skills",
|
|
852
|
+
description: "list load_skill modules (bundled + project + ~/.bluma)",
|
|
853
|
+
category: "inspect"
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
name: "/tools",
|
|
857
|
+
description: "list native tools (optional filter: /tools grep)",
|
|
858
|
+
category: "inspect"
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
name: "/mcp",
|
|
862
|
+
description: "list MCP tools (optional filter: /mcp fs)",
|
|
863
|
+
category: "inspect"
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
name: "/init",
|
|
867
|
+
description: "run init subagent \u2014 BluMa.md codebase documentation",
|
|
868
|
+
category: "agent"
|
|
869
|
+
}
|
|
750
870
|
];
|
|
871
|
+
var CATEGORY_LABEL = {
|
|
872
|
+
session: "Session & UI",
|
|
873
|
+
inspect: "Inspect",
|
|
874
|
+
agent: "Agent",
|
|
875
|
+
help: "Help"
|
|
876
|
+
};
|
|
877
|
+
function formatSlashHelpLines() {
|
|
878
|
+
const cmds = getSlashCommands();
|
|
879
|
+
const byCat = (cat) => cmds.filter((c) => c.category === cat);
|
|
880
|
+
const lines = [];
|
|
881
|
+
for (const cat of ["help", "session", "agent", "inspect"]) {
|
|
882
|
+
const group = byCat(cat);
|
|
883
|
+
if (group.length === 0) continue;
|
|
884
|
+
lines.push(`${CATEGORY_LABEL[cat]}:`);
|
|
885
|
+
for (const c of group) {
|
|
886
|
+
lines.push(` ${c.name.padEnd(14)} ${c.description}`);
|
|
887
|
+
}
|
|
888
|
+
lines.push("");
|
|
889
|
+
}
|
|
890
|
+
return lines;
|
|
891
|
+
}
|
|
751
892
|
var filterSlashCommands = (query) => {
|
|
752
893
|
const list = getSlashCommands();
|
|
753
894
|
const q = (query || "").toLowerCase();
|
|
@@ -1006,7 +1147,9 @@ var SlashSuggestions = memo2(({
|
|
|
1006
1147
|
s.name,
|
|
1007
1148
|
" ",
|
|
1008
1149
|
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
1009
|
-
"
|
|
1150
|
+
"[",
|
|
1151
|
+
s.category,
|
|
1152
|
+
"] ",
|
|
1010
1153
|
s.description
|
|
1011
1154
|
] })
|
|
1012
1155
|
] })
|
|
@@ -1014,13 +1157,13 @@ var SlashSuggestions = memo2(({
|
|
|
1014
1157
|
}) }));
|
|
1015
1158
|
SlashSuggestions.displayName = "SlashSuggestions";
|
|
1016
1159
|
var InputRuleLine = memo2(() => {
|
|
1017
|
-
const { stdout } =
|
|
1160
|
+
const { stdout } = useStdout2();
|
|
1018
1161
|
const cols = stdout?.columns ?? 80;
|
|
1019
1162
|
const n = Math.max(8, cols);
|
|
1020
1163
|
return /* @__PURE__ */ jsx2(Text2, { color: "white", children: TERMINAL_RULE_CHAR.repeat(n) });
|
|
1021
1164
|
});
|
|
1022
1165
|
InputRuleLine.displayName = "InputRuleLine";
|
|
1023
|
-
var Footer = memo2(({ isReadOnly }) => /* @__PURE__ */ jsx2(Box2, { justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: isReadOnly ? "ctrl+c to exit | Enter to send message | Shift+Enter for new line | esc interrupt | Ctrl+O expand last clip" : "ctrl+c to exit | Enter to submit | Shift+Enter for new line | /help
|
|
1166
|
+
var Footer = memo2(({ isReadOnly }) => /* @__PURE__ */ jsx2(Box2, { justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: isReadOnly ? "ctrl+c to exit | Enter to send message | Shift+Enter for new line | esc interrupt | Ctrl+O expand last clip" : "ctrl+c to exit | Enter to submit | Shift+Enter for new line | ? /help \xB7 /img \xB7 esc \xB7 Ctrl+O expand" }) }));
|
|
1024
1167
|
Footer.displayName = "Footer";
|
|
1025
1168
|
var TextLinesRenderer = memo2(({
|
|
1026
1169
|
lines,
|
|
@@ -1058,7 +1201,7 @@ var InputPrompt = memo2(({
|
|
|
1058
1201
|
onInterrupt,
|
|
1059
1202
|
disableWhileProcessing = false
|
|
1060
1203
|
}) => {
|
|
1061
|
-
const { stdout } =
|
|
1204
|
+
const { stdout } = useStdout2();
|
|
1062
1205
|
const [viewWidth] = useState2(() => stdout.columns - 6);
|
|
1063
1206
|
const [slashOpen, setSlashOpen] = useState2(false);
|
|
1064
1207
|
const [slashIndex, setSlashIndex] = useState2(0);
|
|
@@ -1583,8 +1726,8 @@ var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
|
|
|
1583
1726
|
|
|
1584
1727
|
// src/app/agent/agent.ts
|
|
1585
1728
|
import * as dotenv from "dotenv";
|
|
1586
|
-
import
|
|
1587
|
-
import
|
|
1729
|
+
import path18 from "path";
|
|
1730
|
+
import os13 from "os";
|
|
1588
1731
|
|
|
1589
1732
|
// src/app/agent/tool_invoker.ts
|
|
1590
1733
|
import { promises as fs9 } from "fs";
|
|
@@ -1593,7 +1736,7 @@ import { fileURLToPath } from "url";
|
|
|
1593
1736
|
|
|
1594
1737
|
// src/app/agent/tools/natives/edit.ts
|
|
1595
1738
|
import path3 from "path";
|
|
1596
|
-
import
|
|
1739
|
+
import os2 from "os";
|
|
1597
1740
|
import { promises as fs2 } from "fs";
|
|
1598
1741
|
import { diffLines } from "diff";
|
|
1599
1742
|
var MAX_DIFF_SIZE = 5e4;
|
|
@@ -1601,7 +1744,7 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
|
1601
1744
|
function normalizePath(filePath) {
|
|
1602
1745
|
try {
|
|
1603
1746
|
filePath = filePath.trim();
|
|
1604
|
-
if (
|
|
1747
|
+
if (os2.platform() === "win32") {
|
|
1605
1748
|
const winDriveRegex = /^\/([a-zA-Z])[:/]/;
|
|
1606
1749
|
const match = filePath.match(winDriveRegex);
|
|
1607
1750
|
if (match) {
|
|
@@ -3028,12 +3171,12 @@ init_async_command();
|
|
|
3028
3171
|
// src/app/agent/tools/natives/task_boundary.ts
|
|
3029
3172
|
import path9 from "path";
|
|
3030
3173
|
import { promises as fs7 } from "fs";
|
|
3031
|
-
import
|
|
3174
|
+
import os4 from "os";
|
|
3032
3175
|
var currentTask = null;
|
|
3033
3176
|
var artifactsDir = null;
|
|
3034
3177
|
async function getArtifactsDir() {
|
|
3035
3178
|
if (artifactsDir) return artifactsDir;
|
|
3036
|
-
const homeDir =
|
|
3179
|
+
const homeDir = os4.homedir();
|
|
3037
3180
|
const baseDir = path9.join(homeDir, ".bluma", "artifacts");
|
|
3038
3181
|
const sessionId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
|
|
3039
3182
|
artifactsDir = path9.join(baseDir, sessionId);
|
|
@@ -3526,7 +3669,7 @@ ${skill.content}`;
|
|
|
3526
3669
|
// src/app/agent/tools/natives/coding_memory.ts
|
|
3527
3670
|
import * as fs8 from "fs";
|
|
3528
3671
|
import * as path10 from "path";
|
|
3529
|
-
import
|
|
3672
|
+
import os5 from "os";
|
|
3530
3673
|
var PROMPT_DEFAULT_MAX_TOTAL = 1e4;
|
|
3531
3674
|
var PROMPT_DEFAULT_MAX_NOTES = 25;
|
|
3532
3675
|
var PROMPT_DEFAULT_PREVIEW = 500;
|
|
@@ -3534,7 +3677,7 @@ function readCodingMemoryForPrompt(options) {
|
|
|
3534
3677
|
const maxTotal = options?.maxTotalChars ?? PROMPT_DEFAULT_MAX_TOTAL;
|
|
3535
3678
|
const maxNotes = options?.maxNotes ?? PROMPT_DEFAULT_MAX_NOTES;
|
|
3536
3679
|
const preview = options?.previewCharsPerNote ?? PROMPT_DEFAULT_PREVIEW;
|
|
3537
|
-
const globalPath = path10.join(
|
|
3680
|
+
const globalPath = path10.join(os5.homedir(), ".bluma", "coding_memory.json");
|
|
3538
3681
|
const legacyPath = path10.join(process.cwd(), ".bluma", "coding_memory.json");
|
|
3539
3682
|
let raw = null;
|
|
3540
3683
|
try {
|
|
@@ -3579,7 +3722,7 @@ var memoryStore = [];
|
|
|
3579
3722
|
var nextId2 = 1;
|
|
3580
3723
|
var loaded = false;
|
|
3581
3724
|
function getMemoryFilePath() {
|
|
3582
|
-
return path10.join(
|
|
3725
|
+
return path10.join(os5.homedir(), ".bluma", "coding_memory.json");
|
|
3583
3726
|
}
|
|
3584
3727
|
function getLegacyMemoryFilePath() {
|
|
3585
3728
|
return path10.join(process.cwd(), ".bluma", "coding_memory.json");
|
|
@@ -3889,7 +4032,7 @@ var ToolInvoker = class {
|
|
|
3889
4032
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
3890
4033
|
import { promises as fs10 } from "fs";
|
|
3891
4034
|
import path12 from "path";
|
|
3892
|
-
import
|
|
4035
|
+
import os6 from "os";
|
|
3893
4036
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3894
4037
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3895
4038
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -3918,7 +4061,7 @@ var MCPClient = class {
|
|
|
3918
4061
|
const __filename = fileURLToPath2(import.meta.url);
|
|
3919
4062
|
const __dirname2 = path12.dirname(__filename);
|
|
3920
4063
|
const defaultConfigPath = path12.resolve(__dirname2, "config", "bluma-mcp.json");
|
|
3921
|
-
const userConfigPath = path12.join(
|
|
4064
|
+
const userConfigPath = path12.join(os6.homedir(), ".bluma", "bluma-mcp.json");
|
|
3922
4065
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
3923
4066
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
3924
4067
|
const mergedConfig = {
|
|
@@ -3971,7 +4114,7 @@ var MCPClient = class {
|
|
|
3971
4114
|
async connectToStdioServer(serverName, config2) {
|
|
3972
4115
|
let commandToExecute = config2.command;
|
|
3973
4116
|
let argsToExecute = config2.args || [];
|
|
3974
|
-
const isWindows =
|
|
4117
|
+
const isWindows = os6.platform() === "win32";
|
|
3975
4118
|
if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
|
|
3976
4119
|
if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
|
|
3977
4120
|
commandToExecute = argsToExecute[1];
|
|
@@ -4087,12 +4230,12 @@ var AdvancedFeedbackSystem = class {
|
|
|
4087
4230
|
};
|
|
4088
4231
|
|
|
4089
4232
|
// src/app/agent/bluma/core/bluma.ts
|
|
4090
|
-
import
|
|
4233
|
+
import path17 from "path";
|
|
4091
4234
|
import { v4 as uuidv43 } from "uuid";
|
|
4092
4235
|
|
|
4093
4236
|
// src/app/agent/session_manager/session_manager.ts
|
|
4094
4237
|
import path13 from "path";
|
|
4095
|
-
import
|
|
4238
|
+
import os7 from "os";
|
|
4096
4239
|
import { promises as fs11 } from "fs";
|
|
4097
4240
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
4098
4241
|
async function withFileLock(file, fn) {
|
|
@@ -4129,12 +4272,12 @@ function debouncedSave(sessionFile, history, memory) {
|
|
|
4129
4272
|
function expandHome(p) {
|
|
4130
4273
|
if (!p) return p;
|
|
4131
4274
|
if (p.startsWith("~")) {
|
|
4132
|
-
return path13.join(
|
|
4275
|
+
return path13.join(os7.homedir(), p.slice(1));
|
|
4133
4276
|
}
|
|
4134
4277
|
return p;
|
|
4135
4278
|
}
|
|
4136
4279
|
function getPreferredAppDir() {
|
|
4137
|
-
const fixed = path13.join(
|
|
4280
|
+
const fixed = path13.join(os7.homedir(), ".bluma");
|
|
4138
4281
|
return path13.resolve(expandHome(fixed));
|
|
4139
4282
|
}
|
|
4140
4283
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
@@ -4277,7 +4420,7 @@ async function saveSessionHistory(sessionFile, history, memory) {
|
|
|
4277
4420
|
}
|
|
4278
4421
|
|
|
4279
4422
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
4280
|
-
import
|
|
4423
|
+
import os9 from "os";
|
|
4281
4424
|
import fs13 from "fs";
|
|
4282
4425
|
import path15 from "path";
|
|
4283
4426
|
import { execSync } from "child_process";
|
|
@@ -4285,7 +4428,7 @@ import { execSync } from "child_process";
|
|
|
4285
4428
|
// src/app/agent/skills/skill_loader.ts
|
|
4286
4429
|
import fs12 from "fs";
|
|
4287
4430
|
import path14 from "path";
|
|
4288
|
-
import
|
|
4431
|
+
import os8 from "os";
|
|
4289
4432
|
var SkillLoader = class _SkillLoader {
|
|
4290
4433
|
bundledSkillsDir;
|
|
4291
4434
|
projectSkillsDir;
|
|
@@ -4294,7 +4437,7 @@ var SkillLoader = class _SkillLoader {
|
|
|
4294
4437
|
conflicts = [];
|
|
4295
4438
|
constructor(projectRoot, bundledDir) {
|
|
4296
4439
|
this.projectSkillsDir = path14.join(projectRoot, ".bluma", "skills");
|
|
4297
|
-
this.globalSkillsDir = path14.join(
|
|
4440
|
+
this.globalSkillsDir = path14.join(os8.homedir(), ".bluma", "skills");
|
|
4298
4441
|
this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
|
|
4299
4442
|
}
|
|
4300
4443
|
/**
|
|
@@ -5121,12 +5264,12 @@ In sandbox mode you are a Python-focused, non-interactive, deterministic agent t
|
|
|
5121
5264
|
function getUnifiedSystemPrompt(availableSkills) {
|
|
5122
5265
|
const cwd = process.cwd();
|
|
5123
5266
|
const env = {
|
|
5124
|
-
os_type:
|
|
5125
|
-
os_version:
|
|
5126
|
-
architecture:
|
|
5267
|
+
os_type: os9.type(),
|
|
5268
|
+
os_version: os9.release(),
|
|
5269
|
+
architecture: os9.arch(),
|
|
5127
5270
|
workdir: cwd,
|
|
5128
5271
|
shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
|
|
5129
|
-
username:
|
|
5272
|
+
username: os9.userInfo().username,
|
|
5130
5273
|
current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
5131
5274
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
5132
5275
|
is_git_repo: isGitRepo(cwd) ? "yes" : "no",
|
|
@@ -5592,7 +5735,7 @@ async function createApiContextWindow(fullHistory, currentAnchor, compressedTurn
|
|
|
5592
5735
|
}
|
|
5593
5736
|
|
|
5594
5737
|
// src/app/agent/core/llm/llm.ts
|
|
5595
|
-
import
|
|
5738
|
+
import os10 from "os";
|
|
5596
5739
|
import OpenAI from "openai";
|
|
5597
5740
|
function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
5598
5741
|
const msg = String(userMessage || "").slice(0, 300);
|
|
@@ -5609,7 +5752,7 @@ function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
|
5609
5752
|
}
|
|
5610
5753
|
function getPreferredMacAddress() {
|
|
5611
5754
|
try {
|
|
5612
|
-
const ifaces =
|
|
5755
|
+
const ifaces = os10.networkInterfaces();
|
|
5613
5756
|
for (const name of Object.keys(ifaces)) {
|
|
5614
5757
|
const addrs = ifaces[name];
|
|
5615
5758
|
if (!addrs) continue;
|
|
@@ -5624,7 +5767,7 @@ function getPreferredMacAddress() {
|
|
|
5624
5767
|
} catch {
|
|
5625
5768
|
}
|
|
5626
5769
|
try {
|
|
5627
|
-
return `host:${
|
|
5770
|
+
return `host:${os10.hostname()}`;
|
|
5628
5771
|
} catch {
|
|
5629
5772
|
return "unknown";
|
|
5630
5773
|
}
|
|
@@ -5634,7 +5777,7 @@ function defaultInteractiveCliUserContextInput(sessionId, userMessage) {
|
|
|
5634
5777
|
const machineId = getPreferredMacAddress();
|
|
5635
5778
|
let userName = null;
|
|
5636
5779
|
try {
|
|
5637
|
-
userName =
|
|
5780
|
+
userName = os10.userInfo().username || null;
|
|
5638
5781
|
} catch {
|
|
5639
5782
|
userName = null;
|
|
5640
5783
|
}
|
|
@@ -5961,6 +6104,134 @@ var ToolCallNormalizer = class {
|
|
|
5961
6104
|
}
|
|
5962
6105
|
};
|
|
5963
6106
|
|
|
6107
|
+
// src/app/agent/utils/user_message_images.ts
|
|
6108
|
+
import fs14 from "fs";
|
|
6109
|
+
import os11 from "os";
|
|
6110
|
+
import path16 from "path";
|
|
6111
|
+
var IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp)$/i;
|
|
6112
|
+
var MAX_IMAGE_BYTES = 4 * 1024 * 1024;
|
|
6113
|
+
var MAX_IMAGES = 6;
|
|
6114
|
+
var MIME = {
|
|
6115
|
+
".png": "image/png",
|
|
6116
|
+
".jpg": "image/jpeg",
|
|
6117
|
+
".jpeg": "image/jpeg",
|
|
6118
|
+
".gif": "image/gif",
|
|
6119
|
+
".webp": "image/webp",
|
|
6120
|
+
".bmp": "image/bmp"
|
|
6121
|
+
};
|
|
6122
|
+
function expandUserPath(p) {
|
|
6123
|
+
const t = p.trim();
|
|
6124
|
+
if (t.startsWith("~")) {
|
|
6125
|
+
return path16.join(os11.homedir(), t.slice(1).replace(/^\//, ""));
|
|
6126
|
+
}
|
|
6127
|
+
return t;
|
|
6128
|
+
}
|
|
6129
|
+
function isPathAllowed(absResolved, cwd) {
|
|
6130
|
+
const resolved = path16.normalize(path16.resolve(absResolved));
|
|
6131
|
+
const cwdR = path16.normalize(path16.resolve(cwd));
|
|
6132
|
+
const homeR = path16.normalize(path16.resolve(os11.homedir()));
|
|
6133
|
+
const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path16.sep);
|
|
6134
|
+
const underHome = resolved === homeR || resolved.startsWith(homeR + path16.sep);
|
|
6135
|
+
return underCwd || underHome;
|
|
6136
|
+
}
|
|
6137
|
+
function mimeFor(abs) {
|
|
6138
|
+
const ext = path16.extname(abs).toLowerCase();
|
|
6139
|
+
return MIME[ext] || "application/octet-stream";
|
|
6140
|
+
}
|
|
6141
|
+
function collectImagePathStrings(raw) {
|
|
6142
|
+
const found = [];
|
|
6143
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6144
|
+
const quoted = /(["'])((?:(?!\1).)*?\.(?:png|jpe?g|gif|webp|bmp))\1/gi;
|
|
6145
|
+
for (const m of raw.matchAll(quoted)) {
|
|
6146
|
+
const p = m[2].trim();
|
|
6147
|
+
if (p && !seen.has(p)) {
|
|
6148
|
+
seen.add(p);
|
|
6149
|
+
found.push(p);
|
|
6150
|
+
}
|
|
6151
|
+
}
|
|
6152
|
+
const withoutQuoted = raw.replace(quoted, " ");
|
|
6153
|
+
const tokens = withoutQuoted.split(/\s+/);
|
|
6154
|
+
for (let tok of tokens) {
|
|
6155
|
+
tok = tok.replace(/^[([{]+/, "").replace(/[)\]},;:]+$/, "");
|
|
6156
|
+
if (!tok || !IMAGE_EXT.test(tok)) continue;
|
|
6157
|
+
if (!seen.has(tok)) {
|
|
6158
|
+
seen.add(tok);
|
|
6159
|
+
found.push(tok);
|
|
6160
|
+
}
|
|
6161
|
+
}
|
|
6162
|
+
return found;
|
|
6163
|
+
}
|
|
6164
|
+
function resolveImagePath(candidate, cwd) {
|
|
6165
|
+
const expanded = expandUserPath(candidate);
|
|
6166
|
+
const abs = path16.isAbsolute(expanded) ? path16.normalize(expanded) : path16.normalize(path16.resolve(cwd, expanded));
|
|
6167
|
+
if (!isPathAllowed(abs, cwd)) return null;
|
|
6168
|
+
try {
|
|
6169
|
+
if (!fs14.existsSync(abs) || !fs14.statSync(abs).isFile()) return null;
|
|
6170
|
+
} catch {
|
|
6171
|
+
return null;
|
|
6172
|
+
}
|
|
6173
|
+
if (!IMAGE_EXT.test(abs)) return null;
|
|
6174
|
+
return abs;
|
|
6175
|
+
}
|
|
6176
|
+
function stripImagePathStrings(text, paths) {
|
|
6177
|
+
let out = text;
|
|
6178
|
+
for (const p of paths) {
|
|
6179
|
+
const q = p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6180
|
+
out = out.replace(new RegExp(`["']${q}["']`, "gi"), " ");
|
|
6181
|
+
out = out.replace(new RegExp(`(?<=^|\\s)${q}(?=\\s|$)`, "gi"), " ");
|
|
6182
|
+
}
|
|
6183
|
+
return out.replace(/\s{2,}/g, " ").trim();
|
|
6184
|
+
}
|
|
6185
|
+
function buildUserMessageContent(raw, cwd) {
|
|
6186
|
+
const trimmed = raw.trim();
|
|
6187
|
+
if (!trimmed) return trimmed;
|
|
6188
|
+
const candidates = collectImagePathStrings(trimmed);
|
|
6189
|
+
const resolvedAbs = [];
|
|
6190
|
+
const usedStrings = [];
|
|
6191
|
+
for (const c of candidates) {
|
|
6192
|
+
if (resolvedAbs.length >= MAX_IMAGES) break;
|
|
6193
|
+
const abs = resolveImagePath(c, cwd);
|
|
6194
|
+
if (!abs) continue;
|
|
6195
|
+
try {
|
|
6196
|
+
const st = fs14.statSync(abs);
|
|
6197
|
+
if (st.size > MAX_IMAGE_BYTES) continue;
|
|
6198
|
+
} catch {
|
|
6199
|
+
continue;
|
|
6200
|
+
}
|
|
6201
|
+
resolvedAbs.push(abs);
|
|
6202
|
+
usedStrings.push(c);
|
|
6203
|
+
}
|
|
6204
|
+
if (resolvedAbs.length === 0) {
|
|
6205
|
+
return trimmed;
|
|
6206
|
+
}
|
|
6207
|
+
let textPart = stripImagePathStrings(trimmed, usedStrings);
|
|
6208
|
+
if (!textPart) {
|
|
6209
|
+
textPart = "(User attached image(s) only \u2014 describe, compare, or answer using the image(s).)";
|
|
6210
|
+
}
|
|
6211
|
+
const note = resolvedAbs.map((p) => path16.basename(p)).join(", ");
|
|
6212
|
+
const parts = [
|
|
6213
|
+
{
|
|
6214
|
+
type: "text",
|
|
6215
|
+
text: `${textPart}
|
|
6216
|
+
|
|
6217
|
+
[Attached local image file(s): ${note}]`
|
|
6218
|
+
}
|
|
6219
|
+
];
|
|
6220
|
+
for (const abs of resolvedAbs) {
|
|
6221
|
+
const buf = fs14.readFileSync(abs);
|
|
6222
|
+
const b64 = buf.toString("base64");
|
|
6223
|
+
const mime = mimeFor(abs);
|
|
6224
|
+
parts.push({
|
|
6225
|
+
type: "image_url",
|
|
6226
|
+
image_url: {
|
|
6227
|
+
url: `data:${mime};base64,${b64}`,
|
|
6228
|
+
detail: "auto"
|
|
6229
|
+
}
|
|
6230
|
+
});
|
|
6231
|
+
}
|
|
6232
|
+
return parts;
|
|
6233
|
+
}
|
|
6234
|
+
|
|
5964
6235
|
// src/app/agent/bluma/core/bluma.ts
|
|
5965
6236
|
var BluMaAgent = class {
|
|
5966
6237
|
llm;
|
|
@@ -6072,6 +6343,12 @@ var BluMaAgent = class {
|
|
|
6072
6343
|
getUiToolsDetailed() {
|
|
6073
6344
|
return this.mcpClient.getAvailableToolsDetailed();
|
|
6074
6345
|
}
|
|
6346
|
+
listAvailableSkills() {
|
|
6347
|
+
return this.skillLoader.listAvailable();
|
|
6348
|
+
}
|
|
6349
|
+
getSkillsDirs() {
|
|
6350
|
+
return this.skillLoader.getSkillsDirs();
|
|
6351
|
+
}
|
|
6075
6352
|
async processTurn(userInput, userContextInput) {
|
|
6076
6353
|
this.isInterrupted = false;
|
|
6077
6354
|
this.factorRouterTurnClosed = false;
|
|
@@ -6082,7 +6359,8 @@ var BluMaAgent = class {
|
|
|
6082
6359
|
turnId,
|
|
6083
6360
|
sessionId: userContextInput.sessionId || this.sessionId
|
|
6084
6361
|
};
|
|
6085
|
-
|
|
6362
|
+
const userContent = buildUserMessageContent(inputText, process.cwd());
|
|
6363
|
+
this.history.push({ role: "user", content: userContent });
|
|
6086
6364
|
if (inputText === "/init") {
|
|
6087
6365
|
this.eventBus.emit("dispatch", inputText);
|
|
6088
6366
|
}
|
|
@@ -6235,7 +6513,7 @@ var BluMaAgent = class {
|
|
|
6235
6513
|
|
|
6236
6514
|
${editData.error.display}`;
|
|
6237
6515
|
}
|
|
6238
|
-
const filename =
|
|
6516
|
+
const filename = path17.basename(toolArgs.file_path);
|
|
6239
6517
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
6240
6518
|
} catch (e) {
|
|
6241
6519
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -6493,7 +6771,7 @@ import { v4 as uuidv45 } from "uuid";
|
|
|
6493
6771
|
import { v4 as uuidv44 } from "uuid";
|
|
6494
6772
|
|
|
6495
6773
|
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
6496
|
-
import
|
|
6774
|
+
import os12 from "os";
|
|
6497
6775
|
var SYSTEM_PROMPT2 = `
|
|
6498
6776
|
|
|
6499
6777
|
### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
|
|
@@ -6656,12 +6934,12 @@ Rule Summary:
|
|
|
6656
6934
|
function getInitPrompt() {
|
|
6657
6935
|
const now = /* @__PURE__ */ new Date();
|
|
6658
6936
|
const collectedData = {
|
|
6659
|
-
os_type:
|
|
6660
|
-
os_version:
|
|
6661
|
-
architecture:
|
|
6937
|
+
os_type: os12.type(),
|
|
6938
|
+
os_version: os12.release(),
|
|
6939
|
+
architecture: os12.arch(),
|
|
6662
6940
|
workdir: process.cwd(),
|
|
6663
6941
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
6664
|
-
username:
|
|
6942
|
+
username: os12.userInfo().username || "Unknown",
|
|
6665
6943
|
current_date: now.toISOString().split("T")[0],
|
|
6666
6944
|
// Formato YYYY-MM-DD
|
|
6667
6945
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
@@ -6942,14 +7220,14 @@ var RouteManager = class {
|
|
|
6942
7220
|
this.subAgents = subAgents;
|
|
6943
7221
|
this.core = core;
|
|
6944
7222
|
}
|
|
6945
|
-
registerRoute(
|
|
6946
|
-
this.routeHandlers.set(
|
|
7223
|
+
registerRoute(path21, handler) {
|
|
7224
|
+
this.routeHandlers.set(path21, handler);
|
|
6947
7225
|
}
|
|
6948
7226
|
async handleRoute(payload) {
|
|
6949
7227
|
const inputText = String(payload.content || "").trim();
|
|
6950
7228
|
const { userContext } = payload;
|
|
6951
|
-
for (const [
|
|
6952
|
-
if (inputText ===
|
|
7229
|
+
for (const [path21, handler] of this.routeHandlers) {
|
|
7230
|
+
if (inputText === path21 || inputText.startsWith(`${path21} `)) {
|
|
6953
7231
|
return handler({ content: inputText, userContext });
|
|
6954
7232
|
}
|
|
6955
7233
|
}
|
|
@@ -6958,7 +7236,7 @@ var RouteManager = class {
|
|
|
6958
7236
|
};
|
|
6959
7237
|
|
|
6960
7238
|
// src/app/agent/agent.ts
|
|
6961
|
-
var globalEnvPath =
|
|
7239
|
+
var globalEnvPath = path18.join(os13.homedir(), ".bluma", ".env");
|
|
6962
7240
|
dotenv.config({ path: globalEnvPath });
|
|
6963
7241
|
var Agent = class {
|
|
6964
7242
|
sessionId;
|
|
@@ -7069,6 +7347,13 @@ var Agent = class {
|
|
|
7069
7347
|
getUiToolsDetailed() {
|
|
7070
7348
|
return this.core.getUiToolsDetailed();
|
|
7071
7349
|
}
|
|
7350
|
+
/** Skills list for UI (/skills) — same source as system prompt & load_skill. */
|
|
7351
|
+
listAvailableSkills() {
|
|
7352
|
+
return this.core.listAvailableSkills();
|
|
7353
|
+
}
|
|
7354
|
+
getSkillsDirs() {
|
|
7355
|
+
return this.core.getSkillsDirs();
|
|
7356
|
+
}
|
|
7072
7357
|
async processTurn(userInput, userContextInput) {
|
|
7073
7358
|
const inputText = String(userInput.content || "").trim();
|
|
7074
7359
|
const resolvedUserContext = userContextInput ?? defaultInteractiveCliUserContextInput(this.sessionId, inputText.slice(0, 300));
|
|
@@ -7175,12 +7460,12 @@ var renderShellCommand2 = ({ args }) => {
|
|
|
7175
7460
|
};
|
|
7176
7461
|
var renderLsTool2 = ({ args }) => {
|
|
7177
7462
|
const parsed = parseArgs(args);
|
|
7178
|
-
const
|
|
7463
|
+
const path21 = parsed.directory_path || ".";
|
|
7179
7464
|
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
7180
7465
|
/* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "ls" }),
|
|
7181
7466
|
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
7182
7467
|
" ",
|
|
7183
|
-
|
|
7468
|
+
path21
|
|
7184
7469
|
] })
|
|
7185
7470
|
] });
|
|
7186
7471
|
};
|
|
@@ -7316,7 +7601,7 @@ var renderFindByName = ({ args }) => {
|
|
|
7316
7601
|
var renderGrepSearch = ({ args }) => {
|
|
7317
7602
|
const parsed = parseArgs(args);
|
|
7318
7603
|
const query = parsed.query || "";
|
|
7319
|
-
const
|
|
7604
|
+
const path21 = parsed.path || ".";
|
|
7320
7605
|
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
7321
7606
|
/* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "grep" }),
|
|
7322
7607
|
/* @__PURE__ */ jsxs9(Text9, { color: BLUMA_TERMINAL.brandMagenta, children: [
|
|
@@ -7326,7 +7611,7 @@ var renderGrepSearch = ({ args }) => {
|
|
|
7326
7611
|
] }),
|
|
7327
7612
|
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
7328
7613
|
" ",
|
|
7329
|
-
|
|
7614
|
+
path21
|
|
7330
7615
|
] })
|
|
7331
7616
|
] });
|
|
7332
7617
|
};
|
|
@@ -7895,10 +8180,10 @@ var ToolResultDisplayComponent = ({ toolName, result }) => {
|
|
|
7895
8180
|
] }),
|
|
7896
8181
|
matches.slice(0, 5).map((m, i) => {
|
|
7897
8182
|
const row = m;
|
|
7898
|
-
const
|
|
8183
|
+
const path21 = row.file || row.path || row.name || m;
|
|
7899
8184
|
const line = row.line;
|
|
7900
8185
|
return /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
7901
|
-
String(
|
|
8186
|
+
String(path21),
|
|
7902
8187
|
line != null ? `:${line}` : ""
|
|
7903
8188
|
] }, i);
|
|
7904
8189
|
}),
|
|
@@ -8030,8 +8315,18 @@ var SessionInfoConnectingMCP_default = SessionInfoConnectingMCP;
|
|
|
8030
8315
|
|
|
8031
8316
|
// src/app/ui/components/SlashCommands.tsx
|
|
8032
8317
|
import { Box as Box14, Text as Text13 } from "ink";
|
|
8318
|
+
|
|
8319
|
+
// src/app/ui/constants/historyLayout.ts
|
|
8320
|
+
var HEADER_PANEL_HISTORY_ID = 0;
|
|
8321
|
+
|
|
8322
|
+
// src/app/ui/components/SlashCommands.tsx
|
|
8033
8323
|
import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
8034
|
-
var SlashCommands = ({
|
|
8324
|
+
var SlashCommands = ({
|
|
8325
|
+
input,
|
|
8326
|
+
setHistory,
|
|
8327
|
+
agentRef,
|
|
8328
|
+
onClearRecent
|
|
8329
|
+
}) => {
|
|
8035
8330
|
const [cmd, ...args] = input.slice(1).trim().split(/\s+/);
|
|
8036
8331
|
const outBox = (children) => /* @__PURE__ */ jsx14(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Box14, { paddingLeft: 1, flexDirection: "column", children }) });
|
|
8037
8332
|
const render2 = () => {
|
|
@@ -8039,19 +8334,59 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
8039
8334
|
return null;
|
|
8040
8335
|
}
|
|
8041
8336
|
if (cmd === "help") {
|
|
8042
|
-
const
|
|
8337
|
+
const lines = formatSlashHelpLines();
|
|
8338
|
+
return outBox(
|
|
8339
|
+
/* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
8340
|
+
/* @__PURE__ */ jsxs13(Box14, { marginBottom: 1, children: [
|
|
8341
|
+
/* @__PURE__ */ jsx14(Text13, { bold: true, color: BLUMA_TERMINAL.brandMagenta, children: "Slash commands" }),
|
|
8342
|
+
/* @__PURE__ */ jsx14(Text13, { dimColor: true, children: " \xB7 " }),
|
|
8343
|
+
/* @__PURE__ */ jsx14(Text13, { dimColor: true, children: "put .png/.jpg/.webp paths in a normal message to attach images (project dir or ~)" })
|
|
8344
|
+
] }),
|
|
8345
|
+
/* @__PURE__ */ jsx14(Box14, { flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx14(Text13, { dimColor: line.trim().length > 0, children: line || " " }, i)) })
|
|
8346
|
+
] })
|
|
8347
|
+
);
|
|
8348
|
+
}
|
|
8349
|
+
if (cmd === "skills") {
|
|
8350
|
+
const list = agentRef.current?.listAvailableSkills?.() || [];
|
|
8351
|
+
const dirs = agentRef.current?.getSkillsDirs?.();
|
|
8043
8352
|
return outBox(
|
|
8044
8353
|
/* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
8045
|
-
/* @__PURE__ */
|
|
8046
|
-
|
|
8047
|
-
/* @__PURE__ */
|
|
8048
|
-
|
|
8354
|
+
/* @__PURE__ */ jsxs13(Box14, { marginBottom: 1, children: [
|
|
8355
|
+
/* @__PURE__ */ jsx14(Text13, { bold: true, color: BLUMA_TERMINAL.brandMagenta, children: "Skills (load_skill)" }),
|
|
8356
|
+
/* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
8357
|
+
" \xB7 ",
|
|
8358
|
+
list.length,
|
|
8359
|
+
" available"
|
|
8360
|
+
] })
|
|
8361
|
+
] }),
|
|
8362
|
+
dirs ? /* @__PURE__ */ jsxs13(Box14, { marginBottom: 1, flexDirection: "column", children: [
|
|
8363
|
+
/* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
8364
|
+
"bundled: ",
|
|
8365
|
+
String(dirs.bundled || "")
|
|
8366
|
+
] }),
|
|
8367
|
+
/* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
8368
|
+
"project: ",
|
|
8369
|
+
String(dirs.project || "")
|
|
8370
|
+
] }),
|
|
8371
|
+
/* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
8372
|
+
"global: ",
|
|
8373
|
+
String(dirs.global || "")
|
|
8374
|
+
] })
|
|
8375
|
+
] }) : null,
|
|
8376
|
+
list.length === 0 ? /* @__PURE__ */ jsx14(Text13, { color: "yellow", children: "No skills found (check bundled dist/config/skills)." }) : /* @__PURE__ */ jsx14(Box14, { flexDirection: "column", children: list.map((s, i) => /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginBottom: 1, children: [
|
|
8377
|
+
/* @__PURE__ */ jsx14(Text13, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: s.name }),
|
|
8378
|
+
/* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
8379
|
+
s.source,
|
|
8380
|
+
" \u2014 ",
|
|
8381
|
+
s.description || "\u2014"
|
|
8382
|
+
] })
|
|
8049
8383
|
] }, i)) })
|
|
8050
8384
|
] })
|
|
8051
8385
|
);
|
|
8052
8386
|
}
|
|
8053
8387
|
if (cmd === "clear") {
|
|
8054
|
-
|
|
8388
|
+
onClearRecent?.();
|
|
8389
|
+
setHistory((prev) => prev.filter((item) => item.id === HEADER_PANEL_HISTORY_ID));
|
|
8055
8390
|
return outBox(
|
|
8056
8391
|
/* @__PURE__ */ jsxs13(Box14, { children: [
|
|
8057
8392
|
/* @__PURE__ */ jsx14(Text13, { color: "green", children: "[ok]" }),
|
|
@@ -8195,16 +8530,16 @@ var SlashCommands_default = SlashCommands;
|
|
|
8195
8530
|
// src/app/agent/utils/update_check.ts
|
|
8196
8531
|
import updateNotifier from "update-notifier";
|
|
8197
8532
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8198
|
-
import
|
|
8199
|
-
import
|
|
8533
|
+
import path19 from "path";
|
|
8534
|
+
import fs15 from "fs";
|
|
8200
8535
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
8201
8536
|
function findBlumaPackageJson(startDir) {
|
|
8202
8537
|
let dir = startDir;
|
|
8203
8538
|
for (let i = 0; i < 10; i++) {
|
|
8204
|
-
const candidate =
|
|
8205
|
-
if (
|
|
8539
|
+
const candidate = path19.join(dir, "package.json");
|
|
8540
|
+
if (fs15.existsSync(candidate)) {
|
|
8206
8541
|
try {
|
|
8207
|
-
const raw =
|
|
8542
|
+
const raw = fs15.readFileSync(candidate, "utf8");
|
|
8208
8543
|
const parsed = JSON.parse(raw);
|
|
8209
8544
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
8210
8545
|
return { name: parsed.name, version: parsed.version };
|
|
@@ -8212,7 +8547,7 @@ function findBlumaPackageJson(startDir) {
|
|
|
8212
8547
|
} catch {
|
|
8213
8548
|
}
|
|
8214
8549
|
}
|
|
8215
|
-
const parent =
|
|
8550
|
+
const parent = path19.dirname(dir);
|
|
8216
8551
|
if (parent === dir) break;
|
|
8217
8552
|
dir = parent;
|
|
8218
8553
|
}
|
|
@@ -8225,12 +8560,12 @@ async function checkForUpdates() {
|
|
|
8225
8560
|
}
|
|
8226
8561
|
const binPath = process.argv?.[1];
|
|
8227
8562
|
let pkg = null;
|
|
8228
|
-
if (binPath &&
|
|
8229
|
-
pkg = findBlumaPackageJson(
|
|
8563
|
+
if (binPath && fs15.existsSync(binPath)) {
|
|
8564
|
+
pkg = findBlumaPackageJson(path19.dirname(binPath));
|
|
8230
8565
|
}
|
|
8231
8566
|
if (!pkg) {
|
|
8232
8567
|
const __filename = fileURLToPath3(import.meta.url);
|
|
8233
|
-
const __dirname2 =
|
|
8568
|
+
const __dirname2 = path19.dirname(__filename);
|
|
8234
8569
|
pkg = findBlumaPackageJson(__dirname2);
|
|
8235
8570
|
}
|
|
8236
8571
|
if (!pkg) {
|
|
@@ -8460,7 +8795,12 @@ var SAFE_AUTO_APPROVE_TOOLS = [
|
|
|
8460
8795
|
// Status de comandos (read-only)
|
|
8461
8796
|
"command_status"
|
|
8462
8797
|
];
|
|
8463
|
-
|
|
8798
|
+
function trimRecentActivity(s, max = 72) {
|
|
8799
|
+
const t = String(s ?? "").replace(/\s+/g, " ").trim();
|
|
8800
|
+
if (!t) return "";
|
|
8801
|
+
return t.length <= max ? t : `${t.slice(0, max - 1)}\u2026`;
|
|
8802
|
+
}
|
|
8803
|
+
var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
8464
8804
|
const agentInstance = useRef5(null);
|
|
8465
8805
|
const [history, setHistory] = useState6([]);
|
|
8466
8806
|
const [statusMessage, setStatusMessage] = useState6(
|
|
@@ -8478,6 +8818,7 @@ var AppComponent = ({ eventBus, sessionId }) => {
|
|
|
8478
8818
|
null
|
|
8479
8819
|
);
|
|
8480
8820
|
const [isInitAgentActive, setIsInitAgentActive] = useState6(false);
|
|
8821
|
+
const [recentActivityLine, setRecentActivityLine] = useState6(null);
|
|
8481
8822
|
const alwaysAcceptList = useRef5([]);
|
|
8482
8823
|
const workdir = process.cwd();
|
|
8483
8824
|
const updateCheckRan = useRef5(false);
|
|
@@ -8510,6 +8851,26 @@ var AppComponent = ({ eventBus, sessionId }) => {
|
|
|
8510
8851
|
expandPreviewHotkeyBus.off("expand", appendExpandPreviewToHistory);
|
|
8511
8852
|
};
|
|
8512
8853
|
}, [appendExpandPreviewToHistory]);
|
|
8854
|
+
useEffect7(() => {
|
|
8855
|
+
setHistory((prev) => {
|
|
8856
|
+
const tail = prev.filter((h) => h.id !== HEADER_PANEL_HISTORY_ID);
|
|
8857
|
+
return [
|
|
8858
|
+
{
|
|
8859
|
+
id: HEADER_PANEL_HISTORY_ID,
|
|
8860
|
+
component: /* @__PURE__ */ jsx20(
|
|
8861
|
+
Header,
|
|
8862
|
+
{
|
|
8863
|
+
sessionId,
|
|
8864
|
+
workdir,
|
|
8865
|
+
cliVersion,
|
|
8866
|
+
recentActivitySummary: recentActivityLine
|
|
8867
|
+
}
|
|
8868
|
+
)
|
|
8869
|
+
},
|
|
8870
|
+
...tail
|
|
8871
|
+
];
|
|
8872
|
+
});
|
|
8873
|
+
}, [sessionId, workdir, cliVersion, recentActivityLine]);
|
|
8513
8874
|
const handleInterrupt = useCallback2(() => {
|
|
8514
8875
|
if (!isProcessing) return;
|
|
8515
8876
|
eventBus.emit("user_interrupt");
|
|
@@ -8526,12 +8887,45 @@ var AppComponent = ({ eventBus, sessionId }) => {
|
|
|
8526
8887
|
const handleSubmit = useCallback2(
|
|
8527
8888
|
(text) => {
|
|
8528
8889
|
if (!text || isProcessing || !agentInstance.current) return;
|
|
8890
|
+
if (/^\/img\s+/i.test(text) || /^\/image\s+/i.test(text)) {
|
|
8891
|
+
const payload = text.replace(/^\/img\s+/i, "").replace(/^\/image\s+/i, "").trim();
|
|
8892
|
+
if (!payload) {
|
|
8893
|
+
setHistory((prev) => [
|
|
8894
|
+
...prev,
|
|
8895
|
+
{
|
|
8896
|
+
id: prev.length,
|
|
8897
|
+
component: /* @__PURE__ */ jsx20(ChatMeta, { children: "Usage: /img ./screenshot.png \u2014 optional text after the path is sent too" })
|
|
8898
|
+
}
|
|
8899
|
+
]);
|
|
8900
|
+
return;
|
|
8901
|
+
}
|
|
8902
|
+
setIsProcessing(true);
|
|
8903
|
+
turnStartedAtRef.current = Date.now();
|
|
8904
|
+
const shown = payload.length > 800 ? `${payload.slice(0, 800)}\u2026` : payload;
|
|
8905
|
+
setHistory((prev) => [
|
|
8906
|
+
...prev,
|
|
8907
|
+
{
|
|
8908
|
+
id: prev.length,
|
|
8909
|
+
component: /* @__PURE__ */ jsxs19(ChatUserMessage, { children: [
|
|
8910
|
+
/* @__PURE__ */ jsxs19(Text19, { bold: true, color: "white", children: [
|
|
8911
|
+
"/img",
|
|
8912
|
+
" "
|
|
8913
|
+
] }),
|
|
8914
|
+
/* @__PURE__ */ jsx20(Text19, { dimColor: true, children: shown })
|
|
8915
|
+
] })
|
|
8916
|
+
}
|
|
8917
|
+
]);
|
|
8918
|
+
setRecentActivityLine(`Prompt: ${trimRecentActivity(payload)}`);
|
|
8919
|
+
agentInstance.current.processTurn({ content: payload });
|
|
8920
|
+
return;
|
|
8921
|
+
}
|
|
8529
8922
|
if (text.startsWith("/")) {
|
|
8530
8923
|
const [cmd] = text.slice(1).trim().split(/\s+/);
|
|
8531
8924
|
if (!cmd) {
|
|
8532
8925
|
setIsProcessing(false);
|
|
8533
8926
|
return;
|
|
8534
8927
|
}
|
|
8928
|
+
setRecentActivityLine(`You: ${trimRecentActivity(text)}`);
|
|
8535
8929
|
if (cmd === "init") {
|
|
8536
8930
|
setIsInitAgentActive(true);
|
|
8537
8931
|
setIsProcessing(true);
|
|
@@ -8553,7 +8947,8 @@ var AppComponent = ({ eventBus, sessionId }) => {
|
|
|
8553
8947
|
{
|
|
8554
8948
|
input: text,
|
|
8555
8949
|
setHistory,
|
|
8556
|
-
agentRef: agentInstance
|
|
8950
|
+
agentRef: agentInstance,
|
|
8951
|
+
onClearRecent: () => setRecentActivityLine(null)
|
|
8557
8952
|
}
|
|
8558
8953
|
)
|
|
8559
8954
|
}
|
|
@@ -8578,6 +8973,7 @@ var AppComponent = ({ eventBus, sessionId }) => {
|
|
|
8578
8973
|
] }) })
|
|
8579
8974
|
}
|
|
8580
8975
|
]);
|
|
8976
|
+
setRecentActivityLine(`Shell: ${trimRecentActivity(command)}`);
|
|
8581
8977
|
Promise.resolve().then(() => (init_async_command(), async_command_exports)).then(async ({ runCommandAsync: runCommandAsync2 }) => {
|
|
8582
8978
|
try {
|
|
8583
8979
|
const result = await runCommandAsync2({ command, cwd: workdir });
|
|
@@ -8629,6 +9025,7 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
8629
9025
|
component: /* @__PURE__ */ jsx20(ChatUserMessage, { children: /* @__PURE__ */ jsx20(Text19, { children: displayText }) })
|
|
8630
9026
|
}
|
|
8631
9027
|
]);
|
|
9028
|
+
setRecentActivityLine(`You: ${trimRecentActivity(text)}`);
|
|
8632
9029
|
agentInstance.current.processTurn({ content: text });
|
|
8633
9030
|
},
|
|
8634
9031
|
[isProcessing]
|
|
@@ -8655,7 +9052,6 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
8655
9052
|
[]
|
|
8656
9053
|
);
|
|
8657
9054
|
useEffect7(() => {
|
|
8658
|
-
setHistory([{ id: 0, component: /* @__PURE__ */ jsx20(Header, { sessionId, workdir }) }]);
|
|
8659
9055
|
const initializeAgent = async () => {
|
|
8660
9056
|
try {
|
|
8661
9057
|
agentInstance.current = new Agent(sessionId, eventBus);
|
|
@@ -8724,10 +9120,6 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
8724
9120
|
setToolsCount(parsed.tools);
|
|
8725
9121
|
setMcpStatus("connected");
|
|
8726
9122
|
setIsProcessing(false);
|
|
8727
|
-
setHistory((prev) => {
|
|
8728
|
-
const newHistory = [...prev];
|
|
8729
|
-
return newHistory;
|
|
8730
|
-
});
|
|
8731
9123
|
if (!updateCheckRan.current) {
|
|
8732
9124
|
updateCheckRan.current = true;
|
|
8733
9125
|
Promise.resolve().then(() => checkForUpdates()).then((msg) => {
|
|
@@ -8766,7 +9158,9 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
8766
9158
|
}
|
|
8767
9159
|
);
|
|
8768
9160
|
} else if (parsed.type === "tool_call") {
|
|
8769
|
-
|
|
9161
|
+
if (parsed.tool_name) {
|
|
9162
|
+
setRecentActivityLine(`Tool: ${String(parsed.tool_name)}`);
|
|
9163
|
+
}
|
|
8770
9164
|
newComponent = /* @__PURE__ */ jsx20(
|
|
8771
9165
|
ToolCallDisplay,
|
|
8772
9166
|
{
|
|
@@ -8784,6 +9178,11 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
8784
9178
|
}
|
|
8785
9179
|
);
|
|
8786
9180
|
} else if (parsed.type === "user_overlay") {
|
|
9181
|
+
if (parsed.payload != null && String(parsed.payload).trim()) {
|
|
9182
|
+
setRecentActivityLine(
|
|
9183
|
+
`Context: ${trimRecentActivity(String(parsed.payload))}`
|
|
9184
|
+
);
|
|
9185
|
+
}
|
|
8787
9186
|
newComponent = /* @__PURE__ */ jsx20(ChatUserMessage, { children: /* @__PURE__ */ jsx20(Text19, { dimColor: true, children: parsed.payload }) });
|
|
8788
9187
|
} else if (parsed.type === "reasoning") {
|
|
8789
9188
|
newComponent = /* @__PURE__ */ jsx20(ReasoningDisplay, { reasoning: parsed.content });
|
|
@@ -8974,9 +9373,9 @@ async function runAgentMode() {
|
|
|
8974
9373
|
try {
|
|
8975
9374
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
8976
9375
|
const filePath = args[inputFileIndex + 1];
|
|
8977
|
-
rawPayload =
|
|
9376
|
+
rawPayload = fs16.readFileSync(filePath, "utf-8");
|
|
8978
9377
|
} else {
|
|
8979
|
-
rawPayload =
|
|
9378
|
+
rawPayload = fs16.readFileSync(0, "utf-8");
|
|
8980
9379
|
}
|
|
8981
9380
|
} catch (err) {
|
|
8982
9381
|
writeJsonl({
|
|
@@ -9142,6 +9541,16 @@ async function runAgentMode() {
|
|
|
9142
9541
|
process.exit(1);
|
|
9143
9542
|
}
|
|
9144
9543
|
}
|
|
9544
|
+
function readCliPackageVersion() {
|
|
9545
|
+
try {
|
|
9546
|
+
const base = path20.dirname(fileURLToPath4(import.meta.url));
|
|
9547
|
+
const pkgPath = path20.join(base, "..", "package.json");
|
|
9548
|
+
const j = JSON.parse(fs16.readFileSync(pkgPath, "utf8"));
|
|
9549
|
+
return String(j.version || "0.0.0");
|
|
9550
|
+
} catch {
|
|
9551
|
+
return "0.0.0";
|
|
9552
|
+
}
|
|
9553
|
+
}
|
|
9145
9554
|
function runCliMode() {
|
|
9146
9555
|
const BLUMA_TITLE = process.env.BLUMA_TITLE || "BluMa - NomadEngenuity";
|
|
9147
9556
|
startTitleKeeper(BLUMA_TITLE);
|
|
@@ -9149,7 +9558,8 @@ function runCliMode() {
|
|
|
9149
9558
|
const sessionId = uuidv46();
|
|
9150
9559
|
const props = {
|
|
9151
9560
|
eventBus,
|
|
9152
|
-
sessionId
|
|
9561
|
+
sessionId,
|
|
9562
|
+
cliVersion: readCliPackageVersion()
|
|
9153
9563
|
};
|
|
9154
9564
|
render(React12.createElement(App_default, props));
|
|
9155
9565
|
}
|