@nomad-e/bluma-cli 0.0.35 → 0.0.37
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/LICENSE +13 -17
- package/README.md +2 -2
- package/dist/config/native_tools.json +34 -34
- package/dist/main.js +147 -209
- package/package.json +2 -2
package/LICENSE
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
2
4
|
|
|
3
|
-
Copyright
|
|
5
|
+
Copyright 2025 Alex Fonseca and NomadEngenuity contributors
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
copies or substantial portions of the Software.
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# BluMa CLI
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/bluma)
|
|
4
|
-
[](LICENSE)
|
|
5
5
|
[](https://shields.io/)
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -264,6 +264,6 @@ Advanced config files are located in `src/app/agent/config/`.
|
|
|
264
264
|
---
|
|
265
265
|
|
|
266
266
|
## <a name="license"></a>License
|
|
267
|
-
|
|
267
|
+
Apache-2.0. Made by Alex Fonseca and NomadEngenuity contributors.
|
|
268
268
|
|
|
269
269
|
Enjoy, hack, and—if possible—contribute!
|
|
@@ -33,40 +33,40 @@
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
36
|
+
{
|
|
37
|
+
"type": "function",
|
|
38
|
+
"function": {
|
|
39
|
+
"name": "edit_tool",
|
|
40
|
+
"description": "Safely and precisely replaces text in a file, or creates a new file. The tool is resilient to common formatting issues but performs best with precise input.\n\n**Best Practices for Success:**\n1. **Use a read tool first:** Always read the file to get the exact content before generating the `old_string`.\n2. **Provide Context:** For `old_string`, provide a unique, multi-line segment of the file. Including 3+ lines of context around the target change is highly recommended to ensure precision.\n3. **Create New Files:** To create a new file, provide the full `file_path` and an empty string for `old_string`.",
|
|
41
|
+
"parameters": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"file_path": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "The absolute or relative path to the file. The tool will correctly resolve the path for the current operating system."
|
|
47
|
+
},
|
|
48
|
+
"old_string": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"description": "The exact text to be replaced. To ensure accuracy, this should be a unique, multi-line segment from the file, including all original indentation and whitespace. Do not manually escape newlines (use literal newlines, not '\\n'). For creating a new file, this must be an empty string."
|
|
51
|
+
},
|
|
52
|
+
"new_string": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"description": "The new text that will replace `old_string`. Match the indentation and formatting of the surrounding code to maintain code quality. Do not manually escape newlines."
|
|
55
|
+
},
|
|
56
|
+
"expected_replacements": {
|
|
57
|
+
"type": "integer",
|
|
58
|
+
"description": "Optional. The number of occurrences to replace. Defaults to 1. If you expect to replace multiple instances of `old_string`, set this value accordingly.",
|
|
59
|
+
"default": 1
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"required": [
|
|
63
|
+
"file_path",
|
|
64
|
+
"old_string",
|
|
65
|
+
"new_string"
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
70
|
{
|
|
71
71
|
"type": "function",
|
|
72
72
|
"function": {
|
package/dist/main.js
CHANGED
|
@@ -14,9 +14,9 @@ import { Box, Text } from "ink";
|
|
|
14
14
|
import BigText from "ink-big-text";
|
|
15
15
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
16
16
|
var BRAND_COLORS = {
|
|
17
|
-
main: "
|
|
18
|
-
accent: "
|
|
19
|
-
shadow: "
|
|
17
|
+
main: "magenta",
|
|
18
|
+
accent: "blue",
|
|
19
|
+
shadow: "magenta",
|
|
20
20
|
greydark: "#444"
|
|
21
21
|
};
|
|
22
22
|
var Header = () => {
|
|
@@ -35,15 +35,15 @@ var Header = () => {
|
|
|
35
35
|
/* @__PURE__ */ jsx(Text, { children: "2. Be as clear and specific as possible to get accurate responses." }),
|
|
36
36
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
37
37
|
"3. Run ",
|
|
38
|
-
/* @__PURE__ */ jsx(Text, { color: "
|
|
38
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", children: "/init" }),
|
|
39
39
|
" to create a",
|
|
40
40
|
" ",
|
|
41
|
-
/* @__PURE__ */ jsx(Text, { color: "
|
|
41
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", children: "BluMa.md" }),
|
|
42
42
|
", file with instructions for BluMa."
|
|
43
43
|
] }),
|
|
44
44
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
45
45
|
"4. Type ",
|
|
46
|
-
/* @__PURE__ */ jsx(Text, { color: "
|
|
46
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", children: "/help" }),
|
|
47
47
|
" to explore available commands and features."
|
|
48
48
|
] })
|
|
49
49
|
] })
|
|
@@ -487,7 +487,7 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
|
|
|
487
487
|
const cmd = choice.name;
|
|
488
488
|
setSlashOpen(false);
|
|
489
489
|
try {
|
|
490
|
-
setText(`${cmd}
|
|
490
|
+
setText(`${cmd} `);
|
|
491
491
|
} catch (e) {
|
|
492
492
|
permissiveOnSubmit(`${cmd} `);
|
|
493
493
|
}
|
|
@@ -536,7 +536,7 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
|
|
|
536
536
|
// Modo bloqueado visualmente, mantendo hooks estáveis
|
|
537
537
|
/* @__PURE__ */ jsx2(Fragment, { children: /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor: "gray", borderDimColor: true, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
|
|
538
538
|
/* @__PURE__ */ jsxs2(Text2, { color: "white", children: [
|
|
539
|
-
"
|
|
539
|
+
"\u276F",
|
|
540
540
|
" "
|
|
541
541
|
] }),
|
|
542
542
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "ctrl+c to exit" })
|
|
@@ -544,7 +544,7 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
|
|
|
544
544
|
) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
545
545
|
/* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor, borderDimColor: !isReadOnly, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
|
|
546
546
|
/* @__PURE__ */ jsxs2(Text2, { color: "white", children: [
|
|
547
|
-
"
|
|
547
|
+
"\u276F",
|
|
548
548
|
" "
|
|
549
549
|
] }),
|
|
550
550
|
/* @__PURE__ */ jsx2(Text2, { children: textBeforeCursor }),
|
|
@@ -558,20 +558,20 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt, disableWhileProcessing =
|
|
|
558
558
|
let start = Math.max(0, sel - Math.floor(VISIBLE / 2));
|
|
559
559
|
if (start + VISIBLE > total) start = Math.max(0, total - VISIBLE);
|
|
560
560
|
const windowItems = pathAutocomplete.suggestions.slice(start, start + VISIBLE);
|
|
561
|
-
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, height: Math.min(VISIBLE, total),
|
|
561
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, height: Math.min(VISIBLE, total), children: windowItems.map((s, idx) => {
|
|
562
562
|
const realIdx = start + idx;
|
|
563
563
|
const isSelected = realIdx === pathAutocomplete.selected;
|
|
564
564
|
return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
|
|
565
|
-
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "
|
|
566
|
-
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "
|
|
565
|
+
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "magenta" : "gray", children: isSelected ? "\u276F " : " " }),
|
|
566
|
+
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "magenta" : "white", bold: isSelected, dimColor: !isSelected, children: s.label })
|
|
567
567
|
] }, s.fullPath);
|
|
568
568
|
}) });
|
|
569
569
|
})(),
|
|
570
570
|
slashOpen && slashSuggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: slashSuggestions.map((s, idx) => {
|
|
571
571
|
const isSelected = idx === slashIndex;
|
|
572
572
|
return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
|
|
573
|
-
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "
|
|
574
|
-
/* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "
|
|
573
|
+
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "magenta" : "gray", children: isSelected ? "\u276F " : " " }),
|
|
574
|
+
/* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "magenta" : "white", bold: isSelected, dimColor: !isSelected, children: [
|
|
575
575
|
s.name,
|
|
576
576
|
" ",
|
|
577
577
|
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
@@ -624,11 +624,11 @@ var InteractiveMenuComponent = ({ onDecision }) => {
|
|
|
624
624
|
return (
|
|
625
625
|
// Adicionando um pequeno espaçamento vertical entre cada opção também
|
|
626
626
|
/* @__PURE__ */ jsxs3(Box3, { paddingLeft: 1, paddingY: 0, children: [
|
|
627
|
-
/* @__PURE__ */ jsx3(Text3, { color: isSelected ? "
|
|
627
|
+
/* @__PURE__ */ jsx3(Text3, { color: isSelected ? "magenta" : "gray", children: isSelected ? "\u276F " : " " }),
|
|
628
628
|
/* @__PURE__ */ jsx3(
|
|
629
629
|
Text3,
|
|
630
630
|
{
|
|
631
|
-
color: isSelected ? "
|
|
631
|
+
color: isSelected ? "magenta" : "white",
|
|
632
632
|
bold: isSelected,
|
|
633
633
|
dimColor: !isSelected,
|
|
634
634
|
children: option.label
|
|
@@ -672,7 +672,7 @@ var SimpleDiff = ({ text, maxHeight }) => {
|
|
|
672
672
|
} else if (line.startsWith("-")) {
|
|
673
673
|
color = "red";
|
|
674
674
|
} else if (line.startsWith("@@")) {
|
|
675
|
-
color = "
|
|
675
|
+
color = "magenta";
|
|
676
676
|
}
|
|
677
677
|
return /* @__PURE__ */ jsx4(Text4, { color, children: line === "" ? " " : line }, index);
|
|
678
678
|
})
|
|
@@ -696,7 +696,7 @@ var renderShellCommand = ({
|
|
|
696
696
|
}
|
|
697
697
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
|
|
698
698
|
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Shell Command" }) }),
|
|
699
|
-
/* @__PURE__ */ jsx5(Box5, { paddingX: 2, children: /* @__PURE__ */ jsx5(Text5, { children: /* @__PURE__ */ jsx5(Text5, { color: "
|
|
699
|
+
/* @__PURE__ */ jsx5(Box5, { paddingX: 2, children: /* @__PURE__ */ jsx5(Text5, { children: /* @__PURE__ */ jsx5(Text5, { color: "magenta", children: command }) }) })
|
|
700
700
|
] });
|
|
701
701
|
};
|
|
702
702
|
var renderLsTool = ({ toolCall }) => {
|
|
@@ -709,8 +709,8 @@ var renderLsTool = ({ toolCall }) => {
|
|
|
709
709
|
}
|
|
710
710
|
const finalDirectoryName = getBasePath(directoryPath);
|
|
711
711
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
|
|
712
|
-
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: "ls
|
|
713
|
-
/* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsx5(Box5, { paddingX: 2, children: /* @__PURE__ */ jsx5(Text5, { children: /* @__PURE__ */ jsx5(Text5, { color: "
|
|
712
|
+
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: "ls" }) }),
|
|
713
|
+
/* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsx5(Box5, { paddingX: 2, children: /* @__PURE__ */ jsx5(Text5, { children: /* @__PURE__ */ jsx5(Text5, { color: "magenta", children: finalDirectoryName }) }) }) })
|
|
714
714
|
] });
|
|
715
715
|
};
|
|
716
716
|
var renderCountFilesLinesTool = ({ toolCall }) => {
|
|
@@ -726,7 +726,7 @@ var renderCountFilesLinesTool = ({ toolCall }) => {
|
|
|
726
726
|
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Count File Lines" }) }),
|
|
727
727
|
/* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsx5(Box5, { paddingX: 2, children: /* @__PURE__ */ jsxs5(Text5, { children: [
|
|
728
728
|
/* @__PURE__ */ jsx5(Text5, { color: "gray", children: "\u21B3 " }),
|
|
729
|
-
/* @__PURE__ */ jsx5(Text5, { color: "
|
|
729
|
+
/* @__PURE__ */ jsx5(Text5, { color: "magenta", children: finalDirectoryName })
|
|
730
730
|
] }) }) })
|
|
731
731
|
] });
|
|
732
732
|
};
|
|
@@ -746,13 +746,13 @@ var renderReadFileLines = ({ toolCall }) => {
|
|
|
746
746
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
|
|
747
747
|
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Read File" }) }),
|
|
748
748
|
/* @__PURE__ */ jsxs5(Box5, { paddingX: 2, flexDirection: "column", children: [
|
|
749
|
-
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { children: /* @__PURE__ */ jsx5(Text5, { color: "
|
|
749
|
+
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { children: /* @__PURE__ */ jsx5(Text5, { color: "magenta", children: finalFileName }) }) }),
|
|
750
750
|
/* @__PURE__ */ jsx5(Box5, { paddingX: 3, children: /* @__PURE__ */ jsxs5(Text5, { children: [
|
|
751
751
|
/* @__PURE__ */ jsx5(Text5, { color: "gray", children: "\u21B3 " }),
|
|
752
752
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "lines " }),
|
|
753
|
-
/* @__PURE__ */ jsx5(Text5, { color: "
|
|
753
|
+
/* @__PURE__ */ jsx5(Text5, { color: "magenta", children: startLine }),
|
|
754
754
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: " to " }),
|
|
755
|
-
/* @__PURE__ */ jsx5(Text5, { color: "
|
|
755
|
+
/* @__PURE__ */ jsx5(Text5, { color: "magenta", children: endLine })
|
|
756
756
|
] }) })
|
|
757
757
|
] })
|
|
758
758
|
] });
|
|
@@ -770,7 +770,7 @@ var renderEditTool = ({ toolCall, preview }) => {
|
|
|
770
770
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
|
|
771
771
|
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsxs5(Text5, { bold: true, children: [
|
|
772
772
|
"Edit ",
|
|
773
|
-
/* @__PURE__ */ jsx5(Text5, { color: "
|
|
773
|
+
/* @__PURE__ */ jsx5(Text5, { color: "magenta", children: finalFileName })
|
|
774
774
|
] }) }),
|
|
775
775
|
preview ? (
|
|
776
776
|
// Não precisamos da borda externa, o SimpleDiff já é claro o suficiente.
|
|
@@ -851,7 +851,7 @@ var ConfirmationPrompt = ({ toolCalls, preview, onDecision }) => {
|
|
|
851
851
|
import OpenAI from "openai";
|
|
852
852
|
import * as dotenv from "dotenv";
|
|
853
853
|
import path9 from "path";
|
|
854
|
-
import
|
|
854
|
+
import os7 from "os";
|
|
855
855
|
|
|
856
856
|
// src/app/agent/tool_invoker.ts
|
|
857
857
|
import { promises as fs6 } from "fs";
|
|
@@ -924,47 +924,67 @@ ${stderr.trim()}`.trim();
|
|
|
924
924
|
|
|
925
925
|
// src/app/agent/tools/natives/edit.ts
|
|
926
926
|
import path3 from "path";
|
|
927
|
+
import os2 from "os";
|
|
927
928
|
import { promises as fs2 } from "fs";
|
|
928
929
|
import { diffLines } from "diff";
|
|
930
|
+
function normalizePath(filePath) {
|
|
931
|
+
if (os2.platform() === "win32") {
|
|
932
|
+
const winDriveRegex = /^\/([a-zA-Z])[:/]/;
|
|
933
|
+
const match = filePath.match(winDriveRegex);
|
|
934
|
+
if (match) {
|
|
935
|
+
const driveLetter = match[1];
|
|
936
|
+
const restOfPath = filePath.substring(match[0].length);
|
|
937
|
+
filePath = `${driveLetter}:\\${restOfPath}`;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return path3.normalize(path3.resolve(filePath));
|
|
941
|
+
}
|
|
929
942
|
function unescapeLlmString(inputString) {
|
|
930
943
|
return inputString.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\r/g, "\r").replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, "\\");
|
|
931
944
|
}
|
|
932
|
-
function ensureCorrectEdit(currentContent,
|
|
933
|
-
let finalOldString =
|
|
934
|
-
let finalNewString =
|
|
945
|
+
function ensureCorrectEdit(currentContent, originalOldString, originalNewString, expectedReplacements) {
|
|
946
|
+
let finalOldString = originalOldString;
|
|
947
|
+
let finalNewString = originalNewString;
|
|
935
948
|
let occurrences = currentContent.split(finalOldString).length - 1;
|
|
936
|
-
if (occurrences
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
949
|
+
if (occurrences > 0) {
|
|
950
|
+
return [finalOldString, finalNewString, occurrences];
|
|
951
|
+
}
|
|
952
|
+
const candidates = [
|
|
953
|
+
unescapeLlmString(originalOldString),
|
|
954
|
+
originalOldString.trim(),
|
|
955
|
+
unescapeLlmString(originalOldString).trim()
|
|
956
|
+
];
|
|
957
|
+
for (const candidate of candidates) {
|
|
958
|
+
if (candidate === originalOldString) continue;
|
|
959
|
+
const candidateOccurrences = currentContent.split(candidate).length - 1;
|
|
960
|
+
if (candidateOccurrences > 0) {
|
|
961
|
+
finalOldString = candidate;
|
|
962
|
+
occurrences = candidateOccurrences;
|
|
963
|
+
if (candidate === originalOldString.trim() || candidate === unescapeLlmString(originalOldString).trim()) {
|
|
964
|
+
finalNewString = originalNewString.trim();
|
|
965
|
+
}
|
|
966
|
+
if (candidate === unescapeLlmString(originalOldString) || candidate === unescapeLlmString(originalOldString).trim()) {
|
|
967
|
+
finalNewString = unescapeLlmString(finalNewString);
|
|
950
968
|
}
|
|
969
|
+
return [finalOldString, finalNewString, occurrences];
|
|
951
970
|
}
|
|
952
971
|
}
|
|
953
|
-
return [
|
|
972
|
+
return [originalOldString, originalNewString, 0];
|
|
954
973
|
}
|
|
955
974
|
async function calculateEdit(filePath, oldString, newString, expectedReplacements) {
|
|
975
|
+
const normalizedFilePath = normalizePath(filePath);
|
|
956
976
|
let currentContent = null;
|
|
957
977
|
let isNewFile = false;
|
|
958
978
|
let error = null;
|
|
959
|
-
let
|
|
960
|
-
let
|
|
979
|
+
let normalizedNewString = newString.replace(/\r\n/g, "\n");
|
|
980
|
+
let normalizedOldString = oldString.replace(/\r\n/g, "\n");
|
|
961
981
|
let occurrences = 0;
|
|
962
982
|
try {
|
|
963
|
-
currentContent = await fs2.readFile(
|
|
983
|
+
currentContent = await fs2.readFile(normalizedFilePath, "utf-8");
|
|
964
984
|
currentContent = currentContent.replace(/\r\n/g, "\n");
|
|
965
985
|
} catch (e) {
|
|
966
986
|
if (e.code !== "ENOENT") {
|
|
967
|
-
error = { display: `Error reading file: ${e.message}`, raw: `Error reading file ${
|
|
987
|
+
error = { display: `Error reading file: ${e.message}`, raw: `Error reading file ${normalizedFilePath}: ${e.message}` };
|
|
968
988
|
return { currentContent, newContent: "", occurrences: 0, error, isNewFile };
|
|
969
989
|
}
|
|
970
990
|
}
|
|
@@ -972,36 +992,34 @@ async function calculateEdit(filePath, oldString, newString, expectedReplacement
|
|
|
972
992
|
if (oldString === "") {
|
|
973
993
|
isNewFile = true;
|
|
974
994
|
occurrences = 1;
|
|
995
|
+
normalizedNewString = unescapeLlmString(normalizedNewString);
|
|
975
996
|
} else {
|
|
976
|
-
error = { display: "File not found. Cannot apply edit. Use an empty old_string to create a new file.", raw: `File not found: ${
|
|
997
|
+
error = { display: "File not found. Cannot apply edit. Use an empty old_string to create a new file.", raw: `File not found: ${normalizedFilePath}` };
|
|
977
998
|
}
|
|
978
999
|
} else {
|
|
979
1000
|
if (oldString === "") {
|
|
980
|
-
error = { display: "Failed to edit. Attempted to create a file that already exists.", raw: `File already exists, cannot create: ${
|
|
1001
|
+
error = { display: "Failed to edit. Attempted to create a file that already exists.", raw: `File already exists, cannot create: ${normalizedFilePath}` };
|
|
981
1002
|
} else {
|
|
982
|
-
[
|
|
1003
|
+
[normalizedOldString, normalizedNewString, occurrences] = ensureCorrectEdit(currentContent, normalizedOldString, normalizedNewString, expectedReplacements);
|
|
983
1004
|
if (occurrences === 0) {
|
|
984
|
-
error = { display: "Failed to edit, could not find the string to replace.", raw: `0 occurrences found for old_string in ${
|
|
1005
|
+
error = { display: "Failed to edit, could not find the string to replace.", raw: `0 occurrences found for old_string in ${normalizedFilePath}. Check whitespace, indentation, and context.` };
|
|
985
1006
|
} else if (occurrences !== expectedReplacements) {
|
|
986
|
-
error = { display: `Failed to edit, expected ${expectedReplacements} occurrence(s) but found ${occurrences}.`, raw: `Expected ${expectedReplacements} but found ${occurrences} for old_string in ${
|
|
1007
|
+
error = { display: `Failed to edit, expected ${expectedReplacements} occurrence(s) but found ${occurrences}.`, raw: `Expected ${expectedReplacements} but found ${occurrences} for old_string in ${normalizedFilePath}` };
|
|
987
1008
|
}
|
|
988
1009
|
}
|
|
989
1010
|
}
|
|
990
1011
|
let newContentResult = "";
|
|
991
1012
|
if (!error) {
|
|
992
1013
|
if (isNewFile) {
|
|
993
|
-
newContentResult =
|
|
1014
|
+
newContentResult = normalizedNewString;
|
|
994
1015
|
} else if (currentContent !== null) {
|
|
995
|
-
newContentResult = currentContent.replaceAll(
|
|
1016
|
+
newContentResult = currentContent.replaceAll(normalizedOldString, normalizedNewString);
|
|
996
1017
|
}
|
|
997
1018
|
}
|
|
998
1019
|
return { currentContent, newContent: newContentResult, occurrences, error, isNewFile };
|
|
999
1020
|
}
|
|
1000
1021
|
function createDiff(filename, oldContent, newContent) {
|
|
1001
|
-
const diff = diffLines(oldContent, newContent, {
|
|
1002
|
-
// `unified: 3` é o padrão para diffs, mostrando 3 linhas de contexto.
|
|
1003
|
-
// `newlineIsToken: true` lida melhor com mudanças de quebra de linha.
|
|
1004
|
-
});
|
|
1022
|
+
const diff = diffLines(oldContent, newContent, {});
|
|
1005
1023
|
let diffString = `--- a/${filename}
|
|
1006
1024
|
+++ b/${filename}
|
|
1007
1025
|
`;
|
|
@@ -1016,55 +1034,28 @@ function createDiff(filename, oldContent, newContent) {
|
|
|
1016
1034
|
}
|
|
1017
1035
|
async function editTool(args) {
|
|
1018
1036
|
const { file_path, old_string, new_string, expected_replacements = 1 } = args;
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
if (file_path.includes("..")) {
|
|
1023
|
-
return { success: false, error: `Invalid parameters: file_path cannot contain '..'.`, file_path };
|
|
1037
|
+
const normalizedFilePath = normalizePath(file_path);
|
|
1038
|
+
if (normalizedFilePath.includes("..")) {
|
|
1039
|
+
return { success: false, error: `Invalid parameters: file_path cannot contain '..'.`, file_path: normalizedFilePath };
|
|
1024
1040
|
}
|
|
1025
1041
|
try {
|
|
1026
|
-
const editData = await calculateEdit(
|
|
1042
|
+
const editData = await calculateEdit(normalizedFilePath, old_string, new_string, expected_replacements);
|
|
1027
1043
|
if (editData.error) {
|
|
1028
|
-
return {
|
|
1029
|
-
success: false,
|
|
1030
|
-
error: `Execution failed: ${editData.error.display}`,
|
|
1031
|
-
details: editData.error.raw,
|
|
1032
|
-
file_path
|
|
1033
|
-
};
|
|
1044
|
+
return { success: false, error: `Execution failed: ${editData.error.display}`, details: editData.error.raw, file_path: normalizedFilePath };
|
|
1034
1045
|
}
|
|
1035
|
-
await fs2.mkdir(path3.dirname(
|
|
1036
|
-
await fs2.writeFile(
|
|
1037
|
-
const relativePath = path3.relative(process.cwd(),
|
|
1038
|
-
const filename = path3.basename(
|
|
1046
|
+
await fs2.mkdir(path3.dirname(normalizedFilePath), { recursive: true });
|
|
1047
|
+
await fs2.writeFile(normalizedFilePath, editData.newContent, "utf-8");
|
|
1048
|
+
const relativePath = path3.relative(process.cwd(), normalizedFilePath);
|
|
1049
|
+
const filename = path3.basename(normalizedFilePath);
|
|
1039
1050
|
if (editData.isNewFile) {
|
|
1040
|
-
return {
|
|
1041
|
-
success: true,
|
|
1042
|
-
file_path,
|
|
1043
|
-
description: `Created new file: ${relativePath}`,
|
|
1044
|
-
message: `Created new file: ${file_path} with the provided content.`,
|
|
1045
|
-
is_new_file: true,
|
|
1046
|
-
occurrences: editData.occurrences,
|
|
1047
|
-
relative_path: relativePath
|
|
1048
|
-
};
|
|
1051
|
+
return { success: true, file_path: normalizedFilePath, description: `Created new file: ${relativePath}`, message: `Created new file: ${normalizedFilePath} with the provided content.`, is_new_file: true, occurrences: editData.occurrences, relative_path: relativePath };
|
|
1049
1052
|
} else {
|
|
1050
1053
|
const finalDiff = createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
1051
|
-
return {
|
|
1052
|
-
|
|
1053
|
-
file_path,
|
|
1054
|
-
description: `Modified ${relativePath} (${editData.occurrences} replacement(s)).`,
|
|
1055
|
-
message: `Successfully modified file: ${file_path}. Diff of changes:
|
|
1056
|
-
${finalDiff}`,
|
|
1057
|
-
is_new_file: false,
|
|
1058
|
-
occurrences: editData.occurrences,
|
|
1059
|
-
relative_path: relativePath
|
|
1060
|
-
};
|
|
1054
|
+
return { success: true, file_path: normalizedFilePath, description: `Modified ${relativePath} (${editData.occurrences} replacement(s)).`, message: `Successfully modified file: ${normalizedFilePath}. Diff of changes:
|
|
1055
|
+
${finalDiff}`, is_new_file: false, occurrences: editData.occurrences, relative_path: relativePath };
|
|
1061
1056
|
}
|
|
1062
1057
|
} catch (e) {
|
|
1063
|
-
return {
|
|
1064
|
-
success: false,
|
|
1065
|
-
error: `An unexpected error occurred during the edit operation: ${e.message}`,
|
|
1066
|
-
file_path
|
|
1067
|
-
};
|
|
1058
|
+
return { success: false, error: `An unexpected error occurred during the edit operation: ${e.message}`, file_path: normalizedFilePath };
|
|
1068
1059
|
}
|
|
1069
1060
|
}
|
|
1070
1061
|
|
|
@@ -1299,7 +1290,7 @@ var ToolInvoker = class {
|
|
|
1299
1290
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
1300
1291
|
import { promises as fs7 } from "fs";
|
|
1301
1292
|
import path6 from "path";
|
|
1302
|
-
import
|
|
1293
|
+
import os3 from "os";
|
|
1303
1294
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1304
1295
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1305
1296
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -1328,7 +1319,7 @@ var MCPClient = class {
|
|
|
1328
1319
|
const __filename = fileURLToPath2(import.meta.url);
|
|
1329
1320
|
const __dirname = path6.dirname(__filename);
|
|
1330
1321
|
const defaultConfigPath = path6.resolve(__dirname, "config", "bluma-mcp.json");
|
|
1331
|
-
const userConfigPath = path6.join(
|
|
1322
|
+
const userConfigPath = path6.join(os3.homedir(), ".bluma-cli", "bluma-mcp.json");
|
|
1332
1323
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
1333
1324
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
1334
1325
|
const mergedConfig = {
|
|
@@ -1381,7 +1372,7 @@ var MCPClient = class {
|
|
|
1381
1372
|
async connectToStdioServer(serverName, config2) {
|
|
1382
1373
|
let commandToExecute = config2.command;
|
|
1383
1374
|
let argsToExecute = config2.args || [];
|
|
1384
|
-
const isWindows =
|
|
1375
|
+
const isWindows = os3.platform() === "win32";
|
|
1385
1376
|
if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
|
|
1386
1377
|
if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
|
|
1387
1378
|
commandToExecute = argsToExecute[1];
|
|
@@ -1501,7 +1492,7 @@ import path8 from "path";
|
|
|
1501
1492
|
|
|
1502
1493
|
// src/app/agent/session_manger/session_manager.ts
|
|
1503
1494
|
import path7 from "path";
|
|
1504
|
-
import
|
|
1495
|
+
import os4 from "os";
|
|
1505
1496
|
import { promises as fs8 } from "fs";
|
|
1506
1497
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
1507
1498
|
async function withFileLock(file, fn) {
|
|
@@ -1520,12 +1511,12 @@ async function withFileLock(file, fn) {
|
|
|
1520
1511
|
function expandHome(p) {
|
|
1521
1512
|
if (!p) return p;
|
|
1522
1513
|
if (p.startsWith("~")) {
|
|
1523
|
-
return path7.join(
|
|
1514
|
+
return path7.join(os4.homedir(), p.slice(1));
|
|
1524
1515
|
}
|
|
1525
1516
|
return p;
|
|
1526
1517
|
}
|
|
1527
1518
|
function getPreferredAppDir() {
|
|
1528
|
-
const fixed = path7.join(
|
|
1519
|
+
const fixed = path7.join(os4.homedir(), ".bluma-cli");
|
|
1529
1520
|
return path7.resolve(expandHome(fixed));
|
|
1530
1521
|
}
|
|
1531
1522
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
@@ -1632,24 +1623,22 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
1632
1623
|
}
|
|
1633
1624
|
|
|
1634
1625
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
1635
|
-
import
|
|
1626
|
+
import os5 from "os";
|
|
1636
1627
|
var SYSTEM_PROMPT = `
|
|
1637
|
-
|
|
1638
1628
|
### IDENTITY AND OBJECTIVE
|
|
1639
|
-
You are BluMa,
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
You operate with the highest standards of professionalism, precision, and safety.
|
|
1629
|
+
You are BluMa, a fully **AUTONOMOUS** AI Software Engineer from NomadEngenuity.
|
|
1630
|
+
Your single objective is to complete the user's request from end-to-end.
|
|
1631
|
+
You operate with maximum precision, efficiency, and autonomy.
|
|
1643
1632
|
---
|
|
1644
1633
|
|
|
1645
1634
|
### CORE DIRECTIVES
|
|
1646
1635
|
|
|
1647
|
-
1.
|
|
1648
|
-
2. **TOOL-
|
|
1649
|
-
3. **
|
|
1650
|
-
4. **
|
|
1651
|
-
5.
|
|
1652
|
-
6.
|
|
1636
|
+
1. **SEND THE FIRST MESSAGE:** As soon as you receive a task you must send a confirmation message in an informal but technical style. By sending this message your end-to-end shift begins without intervention and without interruption from anyone.
|
|
1637
|
+
2. **TOOL-ONLY OPERATION:** All actions are performed via tool calls. You do not output free-form text.
|
|
1638
|
+
3. **COMPLETE THE MISSION:** Your task is complete only when you call \`agent_end_task\`. Execute all necessary steps to reach this final state.
|
|
1639
|
+
4. **REPORT, DON'T ASK:** Use \`message_notify_user\` to report significant progress, status changes, or final results. You do not ask for permission or clarification. You have full authority to proceed.
|
|
1640
|
+
5. **AUTONOMOUS ERROR RECOVERY:** If a tool fails, analyze the error, formulate a recovery strategy (e.g., retry, use an alternative tool, adjust parameters), and execute it. Notify the user of the failure and your recovery action.
|
|
1641
|
+
6. **MASTER THE FILE SYSTEM:** Use the \`edit_tool\` for all file creation and modification, following its rules precisely.
|
|
1653
1642
|
|
|
1654
1643
|
---
|
|
1655
1644
|
|
|
@@ -1665,67 +1654,34 @@ You operate with the highest standards of professionalism, precision, and safety
|
|
|
1665
1654
|
|
|
1666
1655
|
---
|
|
1667
1656
|
|
|
1668
|
-
###
|
|
1657
|
+
### TOOL-SPECIFIC RULES
|
|
1669
1658
|
<message_rules>
|
|
1670
1659
|
- Communicate with user's via message tools instead of direct text responses
|
|
1671
1660
|
- Reply immediately to new user messages before other operations
|
|
1672
|
-
- First
|
|
1661
|
+
- First notfication must be brief
|
|
1673
1662
|
- Notify user's with brief explanation when changing methods or strategies
|
|
1674
|
-
-
|
|
1675
|
-
- Actively use notify for progress updates, but reserve ask for only essential needs to minimize user's disruption and avoid blocking progress
|
|
1663
|
+
- Actively use notify for progress updates
|
|
1676
1664
|
- Must message user's with results and deliverables before upon task completion 'agent_end_task'
|
|
1677
1665
|
</message_rules>
|
|
1678
1666
|
|
|
1679
|
-
---
|
|
1680
|
-
|
|
1681
|
-
<edit_tool_rules>
|
|
1682
|
-
- Use this tool to perform precise text replacements inside files based on exact literal matches.
|
|
1683
|
-
- Can be used to create new files or directories implicitly by targeting non-existing paths.
|
|
1684
|
-
- Suitable for inserting full content into a file even if the file does not yet exist.
|
|
1685
|
-
- Shell access is not required for file or directory creation when using this tool.
|
|
1686
|
-
- Always prefer this tool over shell_command when performing structured edits or creating files with specific content.
|
|
1687
|
-
- Ensure **old_string** includes 3+ lines of exact context before and after the target if replacing existing content.
|
|
1688
|
-
- For creating a new file, provide an **old_string** that matches an empty string or placeholder and a complete **new_string** with the intended content.
|
|
1689
|
-
- When generating or modifying todo.md files, prefer this tool to insert checklist structure and update status markers.
|
|
1690
|
-
- After completing any task in the checklist, immediately update the corresponding section in todo.md using this tool.
|
|
1691
|
-
- Reconstruct the entire file from task planning context if todo.md becomes outdated or inconsistent.
|
|
1692
|
-
- Track all progress related to planning and execution inside todo.md using text replacement only.
|
|
1693
|
-
</edit_tool_rules>
|
|
1694
1667
|
|
|
1695
1668
|
---
|
|
1696
1669
|
|
|
1697
1670
|
### SCOPE & LIMITATIONS
|
|
1671
|
+
- **IN-SCOPE:** All tasks related to software architecture, design, code generation, analysis, and debugging.
|
|
1672
|
+
- **OUT-OF-SCOPE:** You MUST professionally decline non-technical questions, personal advice, or general conversation by using \`message_notify_user\` to state the request is out of scope, then immediately calling \`agent_end_task\`.
|
|
1698
1673
|
|
|
1699
|
-
**1. IN-SCOPE TASKS:**
|
|
1700
|
-
- Software architecture and design.
|
|
1701
|
-
- Code generation, analysis, and debugging.
|
|
1702
|
-
- Using provided tools to complete development objectives.
|
|
1703
|
-
- Creating technical documentation and diagrams.
|
|
1704
|
-
|
|
1705
|
-
**2. OUT-OF-SCOPE TASKS:**
|
|
1706
|
-
You MUST professionally decline to engage with any of the following:
|
|
1707
|
-
- Non-technical questions (e.g., weather, news, general facts).
|
|
1708
|
-
- Personal, financial, or legal advice.
|
|
1709
|
-
- General conversation, opinions, or jokes.
|
|
1710
|
-
- Any task not directly related to software development.
|
|
1711
|
-
|
|
1712
|
-
**3. PROTOCOL FOR OUT-OF-SCOPE REQUESTS:**
|
|
1713
|
-
If a user asks for something that is out-of-scope, follow this exact procedure:
|
|
1714
|
-
1. Do NOT attempt to answer the question.
|
|
1715
|
-
2. Use the \`message_notify_user\` tool.
|
|
1716
|
-
3. Use the \`agent_end_task\` tool.
|
|
1717
1674
|
`;
|
|
1718
1675
|
function getUnifiedSystemPrompt() {
|
|
1719
1676
|
const now = /* @__PURE__ */ new Date();
|
|
1720
1677
|
const collectedData = {
|
|
1721
|
-
os_type:
|
|
1722
|
-
os_version:
|
|
1723
|
-
architecture:
|
|
1678
|
+
os_type: os5.type(),
|
|
1679
|
+
os_version: os5.release(),
|
|
1680
|
+
architecture: os5.arch(),
|
|
1724
1681
|
workdir: process.cwd(),
|
|
1725
1682
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
1726
|
-
username:
|
|
1683
|
+
username: os5.userInfo().username || "Unknown",
|
|
1727
1684
|
current_date: now.toISOString().split("T")[0],
|
|
1728
|
-
// Formato YYYY-MM-DD
|
|
1729
1685
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
1730
1686
|
locale: process.env.LANG || process.env.LC_ALL || "Unknown"
|
|
1731
1687
|
};
|
|
@@ -1740,7 +1696,6 @@ function getUnifiedSystemPrompt() {
|
|
|
1740
1696
|
timezone: "Unknown",
|
|
1741
1697
|
locale: "Unknown",
|
|
1742
1698
|
...collectedData
|
|
1743
|
-
// Os dados coletados sobrescrevem os padrões
|
|
1744
1699
|
};
|
|
1745
1700
|
let formattedPrompt = SYSTEM_PROMPT;
|
|
1746
1701
|
for (const key in finalEnv) {
|
|
@@ -1748,32 +1703,15 @@ function getUnifiedSystemPrompt() {
|
|
|
1748
1703
|
formattedPrompt = formattedPrompt.replace(new RegExp(placeholder, "g"), finalEnv[key]);
|
|
1749
1704
|
}
|
|
1750
1705
|
const AUTONOMY_PROMPT = `
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
3) Tool Orchestration: Always prefer safe, non-destructive analysis steps first (read, diff, dry-run). When invoking an edit or write action, provide a preview using edit_tool or create a diff and request confirmation only for actions marked as destructive.
|
|
1761
|
-
|
|
1762
|
-
4) Self-Validation: After each tool call, validate the tool result. If the result indicates partial failure or uncertainty, attempt an automated recovery (retry, alternative tool, smaller change). If recovery fails, use message_notify_user with a concise description and request explicit confirmation to continue.
|
|
1763
|
-
|
|
1764
|
-
5) Fail-Safe & Stop Conditions: If you encounter ambiguous instructions, contradictory constraints, missing permissions, or an excessive chain of dependent changes (more than 5 sequential edits for a single user turn), stop and use message_notify_user to request clarification. Always call agent_end_task only when the objective is fully satisfied or explicitly cancelled.
|
|
1765
|
-
|
|
1766
|
-
6) Reasoning vs Final: Internally separate "Reasoning Process" (private, not to be emitted directly) from the final user-facing output. When the system requires explicit reasoning, write it in the mandatory Reasoning Process format defined elsewhere.
|
|
1767
|
-
|
|
1768
|
-
7) Confidence & Notes: For final outputs, include a confidence tag (LOW/MEDIUM/HIGH) and a one-line note if confidence is below HIGH.
|
|
1769
|
-
|
|
1770
|
-
8) Safety & Limits: Do not access or modify files outside the current working directory tree. Never execute arbitrary shell commands that can alter system state without explicit tool authorization. Always honor ignored files and patterns provided by the environment.
|
|
1771
|
-
|
|
1772
|
-
`;
|
|
1773
|
-
formattedPrompt = `${formattedPrompt}
|
|
1774
|
-
|
|
1706
|
+
### AUTONOMY MANDATE
|
|
1707
|
+
You are explicitly authorized to operate with full autonomy. You will self-direct to complete the user's objective. Follow these rules strictly:
|
|
1708
|
+
1) **NO CONFIRMATION:** Execute all planned steps without asking for user confirmation. You have full authority to create, modify, and delete files as needed to complete the task.
|
|
1709
|
+
2) **DECOMPOSE AND EXECUTE:** For complex tasks, create a short, internal plan. Execute each step of the plan sequentially. Report progress via \`message_notify_user\`.
|
|
1710
|
+
3) **ORCHESTRATE TOOLS:** Always start with non-destructive analysis (read, list files). When you call \`edit_tool\`, your arguments must be as precise as possible.
|
|
1711
|
+
4) **SELF-VALIDATE AND RECOVER:** After each action, validate the outcome. If it fails, you are responsible for recovering. Reread files for updated context, adjust your plan, and retry.
|
|
1712
|
+
`;
|
|
1713
|
+
return `${formattedPrompt}
|
|
1775
1714
|
${AUTONOMY_PROMPT}`;
|
|
1776
|
-
return formattedPrompt;
|
|
1777
1715
|
}
|
|
1778
1716
|
|
|
1779
1717
|
// src/app/agent/core/context-api/context_manager.ts
|
|
@@ -1980,7 +1918,7 @@ ${editData.error.display}`;
|
|
|
1980
1918
|
const message = response.choices[0].message;
|
|
1981
1919
|
this.history.push(message);
|
|
1982
1920
|
if (message.tool_calls) {
|
|
1983
|
-
const autoApprovedTools = ["agent_end_task", "message_notify_user", "reasoning_nootebook"];
|
|
1921
|
+
const autoApprovedTools = ["agent_end_task", "message_notify_user", "reasoning_nootebook", "ls_tool", "count_file_lines", "read_file_lines"];
|
|
1984
1922
|
const toolToCall = message.tool_calls[0];
|
|
1985
1923
|
const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
|
|
1986
1924
|
if (isSafeTool) {
|
|
@@ -2047,7 +1985,7 @@ function getSubAgentByCommand(cmd) {
|
|
|
2047
1985
|
}
|
|
2048
1986
|
|
|
2049
1987
|
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
2050
|
-
import
|
|
1988
|
+
import os6 from "os";
|
|
2051
1989
|
var SYSTEM_PROMPT2 = `
|
|
2052
1990
|
|
|
2053
1991
|
### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
|
|
@@ -2210,12 +2148,12 @@ Rule Summary:
|
|
|
2210
2148
|
function getInitPrompt() {
|
|
2211
2149
|
const now = /* @__PURE__ */ new Date();
|
|
2212
2150
|
const collectedData = {
|
|
2213
|
-
os_type:
|
|
2214
|
-
os_version:
|
|
2215
|
-
architecture:
|
|
2151
|
+
os_type: os6.type(),
|
|
2152
|
+
os_version: os6.release(),
|
|
2153
|
+
architecture: os6.arch(),
|
|
2216
2154
|
workdir: process.cwd(),
|
|
2217
2155
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
2218
|
-
username:
|
|
2156
|
+
username: os6.userInfo().username || "Unknown",
|
|
2219
2157
|
current_date: now.toISOString().split("T")[0],
|
|
2220
2158
|
// Formato YYYY-MM-DD
|
|
2221
2159
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
@@ -2468,7 +2406,7 @@ var SubAgentsBluMa = class {
|
|
|
2468
2406
|
};
|
|
2469
2407
|
|
|
2470
2408
|
// src/app/agent/agent.ts
|
|
2471
|
-
var globalEnvPath = path9.join(
|
|
2409
|
+
var globalEnvPath = path9.join(os7.homedir(), ".bluma-cli", ".env");
|
|
2472
2410
|
dotenv.config({ path: globalEnvPath });
|
|
2473
2411
|
var Agent = class {
|
|
2474
2412
|
sessionId;
|
|
@@ -2652,7 +2590,7 @@ var renderShellCommand2 = ({
|
|
|
2652
2590
|
] }) }),
|
|
2653
2591
|
/* @__PURE__ */ jsx8(Box8, { marginLeft: 2, paddingX: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2654
2592
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u21B3 " }),
|
|
2655
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
2593
|
+
/* @__PURE__ */ jsx8(Text8, { color: "magenta", children: command })
|
|
2656
2594
|
] }) })
|
|
2657
2595
|
] });
|
|
2658
2596
|
};
|
|
@@ -2668,11 +2606,11 @@ var renderLsTool2 = ({ args }) => {
|
|
|
2668
2606
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2669
2607
|
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
|
|
2670
2608
|
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
|
|
2671
|
-
"ls
|
|
2609
|
+
"ls"
|
|
2672
2610
|
] }) }),
|
|
2673
2611
|
/* @__PURE__ */ jsx8(Box8, { marginLeft: 2, paddingX: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2674
2612
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u21B3 " }),
|
|
2675
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
2613
|
+
/* @__PURE__ */ jsx8(Text8, { color: "magenta", children: finalDirectoryName })
|
|
2676
2614
|
] }) })
|
|
2677
2615
|
] });
|
|
2678
2616
|
};
|
|
@@ -2694,7 +2632,7 @@ var renderCountFilesLines = ({
|
|
|
2694
2632
|
] }) }),
|
|
2695
2633
|
/* @__PURE__ */ jsx8(Box8, { marginLeft: 2, paddingX: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2696
2634
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u21B3 " }),
|
|
2697
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
2635
|
+
/* @__PURE__ */ jsx8(Text8, { color: "magenta", children: finalDirectoryName })
|
|
2698
2636
|
] }) })
|
|
2699
2637
|
] });
|
|
2700
2638
|
};
|
|
@@ -2718,19 +2656,19 @@ var renderReadFileLines2 = ({
|
|
|
2718
2656
|
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2719
2657
|
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
|
|
2720
2658
|
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF " }),
|
|
2721
|
-
"Read File
|
|
2659
|
+
"Read File"
|
|
2722
2660
|
] }) }),
|
|
2723
2661
|
/* @__PURE__ */ jsxs8(Box8, { marginLeft: 2, flexDirection: "column", children: [
|
|
2724
2662
|
/* @__PURE__ */ jsx8(Box8, { marginLeft: 2, paddingX: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2725
2663
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u21B3 " }),
|
|
2726
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
2664
|
+
/* @__PURE__ */ jsx8(Text8, { color: "magenta", children: finalFileName })
|
|
2727
2665
|
] }) }),
|
|
2728
2666
|
/* @__PURE__ */ jsx8(Box8, { marginLeft: 2, paddingX: 4, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2729
2667
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u21B3 " }),
|
|
2730
2668
|
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "lines " }),
|
|
2731
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
2669
|
+
/* @__PURE__ */ jsx8(Text8, { color: "magenta", children: startLine }),
|
|
2732
2670
|
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " to " }),
|
|
2733
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
2671
|
+
/* @__PURE__ */ jsx8(Text8, { color: "magenta", children: endLine })
|
|
2734
2672
|
] }) })
|
|
2735
2673
|
] })
|
|
2736
2674
|
] })
|
|
@@ -2777,8 +2715,8 @@ var renderBlumaNotebook = ({
|
|
|
2777
2715
|
)
|
|
2778
2716
|
);
|
|
2779
2717
|
} catch (e) {
|
|
2780
|
-
return /* @__PURE__ */ jsxs8(Box8, { borderStyle: "round", borderColor: "
|
|
2781
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
2718
|
+
return /* @__PURE__ */ jsxs8(Box8, { borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
|
|
2719
|
+
/* @__PURE__ */ jsx8(Text8, { color: "magenta", bold: true, children: "Thinking (Error)" }),
|
|
2782
2720
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: JSON.stringify(args, null, 2) })
|
|
2783
2721
|
] });
|
|
2784
2722
|
}
|
|
@@ -2802,7 +2740,7 @@ var renderEditToolCall = ({
|
|
|
2802
2740
|
] }) }),
|
|
2803
2741
|
/* @__PURE__ */ jsx8(Box8, { marginLeft: 2, paddingX: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2804
2742
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u21B3 " }),
|
|
2805
|
-
/* @__PURE__ */ jsx8(Text8, { color: "
|
|
2743
|
+
/* @__PURE__ */ jsx8(Text8, { color: "magenta", children: finalFileName })
|
|
2806
2744
|
] }) }),
|
|
2807
2745
|
preview && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(SimpleDiff, { text: preview, maxHeight: Infinity }) })
|
|
2808
2746
|
] });
|
|
@@ -2935,7 +2873,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
2935
2873
|
const cmds = getSlashCommands();
|
|
2936
2874
|
return outBox(
|
|
2937
2875
|
/* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2938
|
-
/* @__PURE__ */ jsx12(Text11, { color: "
|
|
2876
|
+
/* @__PURE__ */ jsx12(Text11, { color: "magenta", bold: true, children: "Available commands" }),
|
|
2939
2877
|
cmds.map((c, i) => /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2940
2878
|
c.name,
|
|
2941
2879
|
" - ",
|
|
@@ -2976,7 +2914,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
2976
2914
|
const colSource = 18;
|
|
2977
2915
|
return outBox(
|
|
2978
2916
|
/* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2979
|
-
/* @__PURE__ */ jsx12(Text11, { color: "
|
|
2917
|
+
/* @__PURE__ */ jsx12(Text11, { color: "magenta", bold: true, children: "MCP Tools" }),
|
|
2980
2918
|
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2981
2919
|
"Total MCP: ",
|
|
2982
2920
|
tools.length,
|
|
@@ -3025,7 +2963,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
|
3025
2963
|
const colSource = 18;
|
|
3026
2964
|
return outBox(
|
|
3027
2965
|
/* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
3028
|
-
/* @__PURE__ */ jsx12(Text11, { color: "
|
|
2966
|
+
/* @__PURE__ */ jsx12(Text11, { color: "magenta", bold: true, children: "Native Tools" }),
|
|
3029
2967
|
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
3030
2968
|
"Total Native: ",
|
|
3031
2969
|
tools.length,
|
|
@@ -3274,7 +3212,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3274
3212
|
// Uma única Box para o espaçamento
|
|
3275
3213
|
/* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: "white", dimColor: true, children: [
|
|
3276
3214
|
/* @__PURE__ */ jsxs13(Text14, { color: "white", children: [
|
|
3277
|
-
"
|
|
3215
|
+
"\u276F",
|
|
3278
3216
|
" "
|
|
3279
3217
|
] }),
|
|
3280
3218
|
displayText
|
|
@@ -3443,8 +3381,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
3443
3381
|
);
|
|
3444
3382
|
} else if (parsed.type === "user_overlay") {
|
|
3445
3383
|
newComponent = /* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
3446
|
-
/* @__PURE__ */ jsxs13(Text14, { color: "
|
|
3447
|
-
"
|
|
3384
|
+
/* @__PURE__ */ jsxs13(Text14, { color: "magenta", children: [
|
|
3385
|
+
"\u276F",
|
|
3448
3386
|
" "
|
|
3449
3387
|
] }),
|
|
3450
3388
|
parsed.payload
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nomad-e/bluma-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"description": "BluMa independent agent for automation and advanced software engineering.",
|
|
5
5
|
"author": "Alex Fonseca",
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
7
|
"bin": {
|
|
8
8
|
"bluma": "dist/main.js"
|
|
9
9
|
},
|