@nomad-e/bluma-cli 0.1.59 → 0.1.60

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.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/dist/main.js +63 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,6 +6,8 @@
6
6
 
7
7
  **BluMa** is a CLI-based model agent for advanced software engineering workflows. Built with React/Ink 5, it provides an interactive terminal interface for LLM-powered automation, code generation, refactoring, and task execution. Features persistent sessions, contextual reasoning, smart feedback, coordinator mode for worker orchestration, and extensible tools/skills architecture.
8
8
 
9
+ > **Credit:** BluMa was conceived and architected by **Alex Fonseca**.
10
+
9
11
  **Current Version:** 0.1.55
10
12
 
11
13
  ---
@@ -777,7 +779,7 @@ Apache 2.0 — see [LICENSE](LICENSE) for details.
777
779
 
778
780
  - **Issues**: [GitHub Issues](https://github.com/nomad-e/bluma-cli/issues)
779
781
  - **Documentation**: This README + `docs/` directory
780
- - **Author**: Alex Fonseca
782
+ - **Author & Architect**: Alex Fonseca (conceived and architected BluMa)
781
783
  - **npm Package**: [@nomad-e/bluma-cli](https://www.npmjs.com/package/@nomad-e/bluma-cli)
782
784
 
783
785
  ### Runtime Modules (v0.1.41+)
package/dist/main.js CHANGED
@@ -221,6 +221,9 @@ __export(async_command_exports, {
221
221
  import os6 from "os";
222
222
  import { spawn as spawn2 } from "child_process";
223
223
  import { v4 as uuidv42 } from "uuid";
224
+ function normalizeCommandId(raw) {
225
+ return String(raw ?? "").trim().replace(/^[#:\s]+/, "");
226
+ }
224
227
  function cleanupOldCommands() {
225
228
  if (runningCommands.size <= MAX_STORED_COMMANDS) return;
226
229
  const commands = Array.from(runningCommands.entries()).filter(([_, cmd]) => cmd.status !== "running").sort((a, b) => (a[1].endTime || 0) - (b[1].endTime || 0));
@@ -370,13 +373,14 @@ async function commandStatus(args) {
370
373
  error: "command_id is required"
371
374
  };
372
375
  }
373
- const entry = runningCommands.get(command_id);
376
+ const normalizedCommandId = normalizeCommandId(command_id);
377
+ const entry = runningCommands.get(normalizedCommandId);
374
378
  if (!entry) {
375
379
  return {
376
380
  success: false,
377
- command_id,
381
+ command_id: normalizedCommandId,
378
382
  status: "not_found",
379
- error: `Command with id "${command_id}" not found. It may have expired or never existed.`
383
+ error: `Command with id "${normalizedCommandId}" not found. It may have expired or never existed.`
380
384
  };
381
385
  }
382
386
  const maxWait = Math.min(wait_seconds, 15);
@@ -408,7 +412,7 @@ async function commandStatus(args) {
408
412
  const duration = entry.endTime ? (entry.endTime - entry.startTime) / 1e3 : (Date.now() - entry.startTime) / 1e3;
409
413
  return {
410
414
  success: true,
411
- command_id,
415
+ command_id: normalizedCommandId,
412
416
  status: entry.status,
413
417
  stdout: stdout || void 0,
414
418
  stderr: stderr || void 0,
@@ -434,11 +438,12 @@ async function sendCommandInput(args) {
434
438
  error: "command_id and input are required"
435
439
  };
436
440
  }
437
- const entry = runningCommands.get(command_id);
441
+ const normalizedCommandId = normalizeCommandId(command_id);
442
+ const entry = runningCommands.get(normalizedCommandId);
438
443
  if (!entry) {
439
444
  return {
440
445
  success: false,
441
- error: `Command with id "${command_id}" not found`
446
+ error: `Command with id "${normalizedCommandId}" not found`
442
447
  };
443
448
  }
444
449
  if (entry.status !== "running" || !entry.process) {
@@ -450,7 +455,7 @@ async function sendCommandInput(args) {
450
455
  entry.process.stdin?.write(input);
451
456
  return {
452
457
  success: true,
453
- message: `Sent ${input.length} characters to command ${command_id}`
458
+ message: `Sent ${input.length} characters to command ${normalizedCommandId}`
454
459
  };
455
460
  } catch (error) {
456
461
  return {
@@ -462,11 +467,12 @@ async function sendCommandInput(args) {
462
467
  async function killCommand(args) {
463
468
  try {
464
469
  const { command_id } = args;
465
- const entry = runningCommands.get(command_id);
470
+ const normalizedCommandId = normalizeCommandId(command_id);
471
+ const entry = runningCommands.get(normalizedCommandId);
466
472
  if (!entry) {
467
473
  return {
468
474
  success: false,
469
- error: `Command with id "${command_id}" not found`
475
+ error: `Command with id "${normalizedCommandId}" not found`
470
476
  };
471
477
  }
472
478
  if (entry.status !== "running" || !entry.process) {
@@ -480,7 +486,7 @@ async function killCommand(args) {
480
486
  entry.endTime = Date.now();
481
487
  return {
482
488
  success: true,
483
- message: `Command ${command_id} killed`
489
+ message: `Command ${normalizedCommandId} killed`
484
490
  };
485
491
  } catch (error) {
486
492
  return {
@@ -3927,7 +3933,7 @@ var renderCommandStatus = ({ args }) => {
3927
3933
  const parsed = parseArgs(args);
3928
3934
  const id = parsed.command_id || "[no id]";
3929
3935
  return /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text7, { color: BLUMA_TERMINAL.muted, children: [
3930
- "#",
3936
+ "id ",
3931
3937
  id
3932
3938
  ] }) });
3933
3939
  };
@@ -10088,6 +10094,14 @@ You (Bluma):
10088
10094
  - **Clean up after yourself** - Remove temporary files when done
10089
10095
  - **Respect session boundaries** - Stay in your session workspace
10090
10096
 
10097
+ ### Job wall-clock timeout (orchestrator) \u2014 read this
10098
+
10099
+ The coordinator sets a **single deadline** for the whole stream (e.g. \`timeout_seconds: 60\`). The timer **starts at job start** and counts **everything**: first LLM call (often **30\u201360s+** of \u201CThinking\u201D), tools, follow-up LLM calls, and your final \`message\`+\`result\`.
10100
+
10101
+ - **60 seconds is usually too short** for \`generate_document\` / PDF / multi-step work \u2014 the job can die **after** \`file_write\` succeeds but **before** \`shell_command\` or \`message(result)\`, with \`Job excedeu 60.0s\` / exit \`-9\`. That is **not** proof the sandbox is broken; it means the **budget was too tight**.
10102
+ - Prefer asking coordinators (or docs) to use **\u2265180s** for document generation, **\u2265300s** for heavy tasks.
10103
+ - When a job times out, **do not** claim \u201Csandbox unavailable\u201D unless you have real infra evidence (connection errors, 5xx). Timeout = **deadline exceeded**, often fixable by **raising \`timeout_seconds\`** on the caller side.
10104
+
10091
10105
  ### You Represent the Platform
10092
10106
 
10093
10107
  - **Severino trusts you** - Don't let him down
@@ -14235,6 +14249,40 @@ var ToolResultDisplayComponent = ({
14235
14249
  }
14236
14250
  return /* @__PURE__ */ jsx12(ResultGutter, { children: /* @__PURE__ */ jsx12(MarkdownRenderer, { markdown: String(body) }) });
14237
14251
  }
14252
+ if (toolName.includes("ask_user_question")) {
14253
+ const success = parsed?.success === true;
14254
+ const selectedLabel = typeof parsed?.selected_label === "string" ? parsed.selected_label : "";
14255
+ const selectedIndex = typeof parsed?.selected_index === "number" ? parsed.selected_index : null;
14256
+ const questionIndex = typeof parsed?.question_index === "number" ? parsed.question_index : 0;
14257
+ const qs = Array.isArray(args?.questions) ? args.questions : [];
14258
+ const q = qs[questionIndex];
14259
+ const questionText = typeof q?.question === "string" ? q.question : "";
14260
+ if (success && selectedLabel) {
14261
+ return /* @__PURE__ */ jsx12(ResultGutter, { children: /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
14262
+ /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
14263
+ /* @__PURE__ */ jsx12(Text12, { bold: true, children: "Response" }),
14264
+ " \xB7 ",
14265
+ selectedLabel
14266
+ ] }),
14267
+ questionText ? /* @__PURE__ */ jsxs12(Text12, { dimColor: true, wrap: "wrap", children: [
14268
+ truncate3(questionText, 140),
14269
+ selectedIndex !== null ? ` \xB7 option ${selectedIndex + 1}` : ""
14270
+ ] }) : null
14271
+ ] }) });
14272
+ }
14273
+ if (parsed?.cancelled === true) {
14274
+ return /* @__PURE__ */ jsx12(ResultGutter, { children: /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
14275
+ /* @__PURE__ */ jsx12(Text12, { bold: true, children: "Response" }),
14276
+ " \xB7 cancelled by user"
14277
+ ] }) });
14278
+ }
14279
+ const err = typeof parsed?.error === "string" ? parsed.error : "";
14280
+ return /* @__PURE__ */ jsx12(ResultGutter, { children: /* @__PURE__ */ jsxs12(Text12, { color: err ? BLUMA_TERMINAL.err : void 0, dimColor: !err, wrap: "wrap", children: [
14281
+ /* @__PURE__ */ jsx12(Text12, { bold: true, children: "Response" }),
14282
+ " \xB7 ",
14283
+ err || "No answer returned"
14284
+ ] }) });
14285
+ }
14238
14286
  if (toolName.includes("file_write") && parsed) {
14239
14287
  return /* @__PURE__ */ jsx12(ResultGutter, { children: /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
14240
14288
  parsed.created ? "Created " : "Wrote to ",
@@ -16043,6 +16091,9 @@ Run: npm i -g ${BLUMA_PACKAGE_NAME} to update.`;
16043
16091
  }
16044
16092
  }
16045
16093
 
16094
+ // src/app/ui/App.tsx
16095
+ init_sandbox_policy();
16096
+
16046
16097
  // src/app/ui/components/UpdateNotice.tsx
16047
16098
  import { Box as Box17, Text as Text16 } from "ink";
16048
16099
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
@@ -16693,7 +16744,7 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
16693
16744
  const [liveToolArgs, setLiveToolArgs] = useState11(void 0);
16694
16745
  const [isReasoning, setIsReasoning] = useState11(false);
16695
16746
  const alwaysAcceptList = useRef6([]);
16696
- const workdir = process.cwd();
16747
+ const workdir = getSandboxPolicy().workspaceRoot;
16697
16748
  const turnStartedAtRef = useRef6(null);
16698
16749
  const [processingStartMs, setProcessingStartMs] = useState11(null);
16699
16750
  const markTurnStarted = useCallback4(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomad-e/bluma-cli",
3
- "version": "0.1.59",
3
+ "version": "0.1.60",
4
4
  "description": "BluMa independent agent for automation and advanced software engineering.",
5
5
  "author": "Alex Fonseca",
6
6
  "license": "Apache-2.0",