@nomad-e/bluma-cli 0.1.45 → 0.1.46

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 (2) hide show
  1. package/dist/main.js +416 -23
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -884,11 +884,17 @@ var useCustomInput = ({
884
884
  return;
885
885
  }
886
886
  if (key.escape) {
887
+ if (globalThis.__BLUMA_SLASH_MENU_OPEN__) {
888
+ return;
889
+ }
887
890
  onInterrupt();
888
891
  return;
889
892
  }
890
893
  if (isReadOnly) {
891
894
  if (key.return && !key.shift) {
895
+ if (globalThis.__BLUMA_SLASH_MENU_OPEN__) {
896
+ return;
897
+ }
892
898
  if (state.text.trim().length > 0) {
893
899
  onSubmit(state.text);
894
900
  dispatch({ type: "SUBMIT" });
@@ -899,6 +905,10 @@ var useCustomInput = ({
899
905
  dispatch({ type: "NEWLINE" });
900
906
  return;
901
907
  }
908
+ if (globalThis.__BLUMA_SLASH_MENU_OPEN__) {
909
+ if (key.tab) return;
910
+ if (key.upArrow || key.downArrow) return;
911
+ }
902
912
  if (key.leftArrow) return dispatch({ type: "MOVE_CURSOR", direction: "left" });
903
913
  if (key.rightArrow) return dispatch({ type: "MOVE_CURSOR", direction: "right" });
904
914
  if (key.upArrow) return dispatch({ type: "MOVE_CURSOR", direction: "up" });
@@ -924,6 +934,7 @@ var useCustomInput = ({
924
934
  }
925
935
  if (key.return) {
926
936
  if (globalThis.__BLUMA_AT_OPEN__) return;
937
+ if (globalThis.__BLUMA_SLASH_MENU_OPEN__) return;
927
938
  if (globalThis.__BLUMA_SUPPRESS_SUBMIT__) {
928
939
  globalThis.__BLUMA_SUPPRESS_SUBMIT__ = false;
929
940
  return;
@@ -934,6 +945,10 @@ var useCustomInput = ({
934
945
  }
935
946
  return;
936
947
  }
948
+ if (globalThis.__BLUMA_SLASH_MENU_OPEN__) {
949
+ if (key.tab) return;
950
+ if (key.upArrow || key.downArrow) return;
951
+ }
937
952
  if (key.leftArrow) return dispatch({ type: "MOVE_CURSOR", direction: "left" });
938
953
  if (key.rightArrow) return dispatch({ type: "MOVE_CURSOR", direction: "right" });
939
954
  if (key.upArrow) return dispatch({ type: "MOVE_CURSOR", direction: "up" });
@@ -951,6 +966,7 @@ var useCustomInput = ({
951
966
  void Promise.resolve(onChordPaste(insertAtCursor));
952
967
  return;
953
968
  }
969
+ if (key.tab && globalThis.__BLUMA_SLASH_MENU_OPEN__) return;
954
970
  if (key.ctrl || key.meta || key.tab) return;
955
971
  inputBuffer.current += input;
956
972
  if (!flushScheduled.current) {
@@ -1090,8 +1106,13 @@ var getSlashCommands = () => [
1090
1106
  },
1091
1107
  {
1092
1108
  name: "/agent",
1093
- description: "coordinator prompt mode: /agent [default|coordinator]",
1094
- category: "inspect"
1109
+ description: "prompt profile (not /agents): /agent [default|coordinator] \u2014 coordinator playbook in system prompt; then /init",
1110
+ category: "agent"
1111
+ },
1112
+ {
1113
+ name: "/agents",
1114
+ description: "list worker/agent sessions (spawn_agent children in this CLI)",
1115
+ category: "agent"
1095
1116
  },
1096
1117
  {
1097
1118
  name: "/features",
@@ -2208,9 +2229,23 @@ var InputPrompt = memo2(({
2208
2229
  const resolved = resolveInlineImageLabels(valueForAgent, map);
2209
2230
  if (isReadOnly) {
2210
2231
  if (trimmed.length > 0) {
2232
+ const routeText = resolved.trim();
2233
+ if (isSlashRoutingLine(routeText)) {
2234
+ onSubmit(resolved);
2235
+ clearInlineImageSlots();
2236
+ return;
2237
+ }
2238
+ if (routeText.startsWith("/")) {
2239
+ uiEventBus.emit("input_notice", {
2240
+ message: "Unknown or incomplete slash command while the agent runs. Type /help for the list, or finish the command name.",
2241
+ ts: Date.now()
2242
+ });
2243
+ clearInlineImageSlots();
2244
+ return;
2245
+ }
2211
2246
  uiEventBus.emit("user_overlay", {
2212
2247
  kind: "message",
2213
- payload: resolved.trim(),
2248
+ payload: routeText,
2214
2249
  ts: Date.now()
2215
2250
  });
2216
2251
  clearInlineImageSlots();
@@ -2302,7 +2337,7 @@ var InputPrompt = memo2(({
2302
2337
  return { lines, cursorLine, cursorCol, totalLines: lines.length };
2303
2338
  }, [text, cursorPosition]);
2304
2339
  const displayData = linesData;
2305
- const placeholder = isReadOnly ? " Press Esc to cancel | Enter message while agent runs" : "";
2340
+ const placeholder = isReadOnly ? " Esc cancel \xB7 / opens slash palette (\u2191\u2193 Tab \xB7 Enter to apply) \xB7 Enter sends" : "";
2306
2341
  const showPlaceholder = text.length === 0 && isReadOnly;
2307
2342
  const slashQuery = useMemo(() => text.startsWith("/") ? text : "", [text]);
2308
2343
  const slashSuggestions = useMemo(() => {
@@ -2310,7 +2345,7 @@ var InputPrompt = memo2(({
2310
2345
  return filterSlashCommands(slashQuery);
2311
2346
  }, [slashQuery]);
2312
2347
  useEffect3(() => {
2313
- if (isReadOnly) {
2348
+ if (disableWhileProcessing) {
2314
2349
  setSlashOpen(false);
2315
2350
  wasSlashMenuOpenRef.current = false;
2316
2351
  return;
@@ -2326,31 +2361,41 @@ var InputPrompt = memo2(({
2326
2361
  wasSlashMenuOpenRef.current = false;
2327
2362
  setSlashOpen(false);
2328
2363
  }
2329
- }, [text, isReadOnly]);
2364
+ }, [text, disableWhileProcessing]);
2330
2365
  useEffect3(() => {
2331
2366
  if (!slashOpen || slashSuggestions.length === 0) return;
2332
2367
  setSlashIndex((i) => Math.min(i, slashSuggestions.length - 1));
2333
2368
  }, [slashOpen, slashSuggestions.length, slashQuery]);
2369
+ useLayoutEffect(() => {
2370
+ const open = slashOpen && slashSuggestions.length > 0 && !disableWhileProcessing && !permissionDialogOpen;
2371
+ globalThis.__BLUMA_SLASH_MENU_OPEN__ = open;
2372
+ return () => {
2373
+ globalThis.__BLUMA_SLASH_MENU_OPEN__ = false;
2374
+ };
2375
+ }, [slashOpen, slashSuggestions.length, disableWhileProcessing, permissionDialogOpen]);
2376
+ const applySlashChoice = (choice) => {
2377
+ if (!choice) return;
2378
+ setSlashOpen(false);
2379
+ try {
2380
+ setText(`${choice.name} `);
2381
+ } catch {
2382
+ permissiveOnSubmit(`${choice.name} `);
2383
+ }
2384
+ };
2334
2385
  useInput2((input, key) => {
2335
- if (!slashOpen) return;
2386
+ if (!slashOpen || slashSuggestions.length === 0) return;
2336
2387
  if (key.downArrow) {
2337
2388
  setSlashIndex((i) => Math.min(i + 1, Math.max(0, slashSuggestions.length - 1)));
2338
2389
  } else if (key.upArrow) {
2339
2390
  setSlashIndex((i) => Math.max(i - 1, 0));
2340
2391
  } else if (key.return) {
2341
- const choice = slashSuggestions[slashIndex];
2342
- if (choice) {
2343
- setSlashOpen(false);
2344
- try {
2345
- setText(`${choice.name} `);
2346
- } catch (e) {
2347
- permissiveOnSubmit(`${choice.name} `);
2348
- }
2349
- }
2392
+ applySlashChoice(slashSuggestions[slashIndex]);
2393
+ } else if (key.tab) {
2394
+ applySlashChoice(slashSuggestions[slashIndex]);
2350
2395
  } else if (key.escape) {
2351
2396
  setSlashOpen(false);
2352
2397
  }
2353
- }, { isActive: slashOpen && !permissionDialogOpen });
2398
+ }, { isActive: slashOpen && slashSuggestions.length > 0 && !permissionDialogOpen && !disableWhileProcessing });
2354
2399
  useEffect3(() => {
2355
2400
  if (globalThis.__BLUMA_FORCE_CURSOR_END__) {
2356
2401
  setText(text, text.length);
@@ -6939,7 +6984,24 @@ function readSessionLog(sessionId) {
6939
6984
  }
6940
6985
 
6941
6986
  // src/app/agent/tools/natives/agent_coordination.ts
6942
- function buildWorkerPayload(sessionId, args, parentSessionId) {
6987
+ function readUserContextFromEnv() {
6988
+ const raw = process.env.BLUMA_USER_CONTEXT_JSON;
6989
+ if (!raw) return null;
6990
+ try {
6991
+ const parsed = JSON.parse(raw);
6992
+ return {
6993
+ conversationId: parsed.conversationId ?? null,
6994
+ userId: parsed.userId ?? null,
6995
+ userName: parsed.userName ?? null,
6996
+ userEmail: parsed.userEmail ?? null,
6997
+ companyId: parsed.companyId ?? null,
6998
+ companyName: parsed.companyName ?? null
6999
+ };
7000
+ } catch {
7001
+ return null;
7002
+ }
7003
+ }
7004
+ function buildWorkerPayload(sessionId, args, parentSessionId, userContext) {
6943
7005
  return {
6944
7006
  message_id: sessionId,
6945
7007
  session_id: sessionId,
@@ -6952,6 +7014,7 @@ function buildWorkerPayload(sessionId, args, parentSessionId) {
6952
7014
  worker_title: args.title || null,
6953
7015
  worker_role: args.agent_type || "worker"
6954
7016
  },
7017
+ user_context: userContext || void 0,
6955
7018
  metadata: {
6956
7019
  sandbox: process.env.BLUMA_SANDBOX === "true",
6957
7020
  sandbox_name: process.env.BLUMA_SANDBOX_NAME || void 0,
@@ -6995,8 +7058,9 @@ async function spawnAgent(args) {
6995
7058
  }
6996
7059
  const sessionId = uuidv43();
6997
7060
  const parentSessionId = process.env.BLUMA_SESSION_ID || null;
7061
+ const userContext = readUserContextFromEnv();
6998
7062
  const title = args.title || `worker:${args.agent_type || "worker"}`;
6999
- const payload = buildWorkerPayload(sessionId, args, parentSessionId);
7063
+ const payload = buildWorkerPayload(sessionId, args, parentSessionId, userContext);
7000
7064
  const payloadDir = fs16.mkdtempSync(path18.join(os10.tmpdir(), "bluma-worker-"));
7001
7065
  const payloadPath = path18.join(payloadDir, `${sessionId}.json`);
7002
7066
  fs16.writeFileSync(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
@@ -7012,7 +7076,9 @@ async function spawnAgent(args) {
7012
7076
  background: true,
7013
7077
  coordinator_worker: true,
7014
7078
  parent_session_id: parentSessionId,
7015
- agent_type: args.agent_type || "worker"
7079
+ agent_type: args.agent_type || "worker",
7080
+ user_id: userContext?.userId ?? null,
7081
+ company_id: userContext?.companyId ?? null
7016
7082
  }
7017
7083
  });
7018
7084
  const child = spawn4(
@@ -8635,6 +8701,212 @@ function getPlugin(name) {
8635
8701
  return listPlugins().find((plugin) => plugin.name === q) || null;
8636
8702
  }
8637
8703
 
8704
+ // src/app/agent/utils/coordinator_prompt.ts
8705
+ var COORDINATOR_SYSTEM_PROMPT = `
8706
+ # BluMa Coordinator Mode
8707
+
8708
+ You are the **BluMa Coordinator** - a worker orchestrator for software engineering.
8709
+
8710
+ ## 1. Your Role
8711
+
8712
+ You do **NOT execute tasks directly**. Your job is to:
8713
+ - **Orchestrate workers** to research, implement, and verify changes
8714
+ - **Synthesize results** and communicate with the user
8715
+ - **Answer questions directly** when possible \u2014 don't delegate work you can handle without tools
8716
+ - **Read-only tools** (\`read_file_lines\`, \`grep\`, etc.) are fine for **light** coordinator checks (e.g. verify a path before writing a worker spec); heavy exploration belongs in workers
8717
+ - **Break complex tasks** into parallelizable work items
8718
+
8719
+ Every message you send is to the **user**. Worker results are internal notifications.
8720
+
8721
+ ## 2. Your Main Tools
8722
+
8723
+ | Tool | Usage |
8724
+ |------|-------|
8725
+ | \`spawn_agent\` | Create a new worker |
8726
+ | \`wait_agent\` | Wait for worker completion |
8727
+ | \`list_agents\` | List active/completed workers |
8728
+
8729
+ ### Tool contract (BluMa)
8730
+
8731
+ - \`spawn_agent\` returns JSON with \`session_id\` (and \`success\`). That id is the only stable handle for follow-up.
8732
+ - Call \`wait_agent\` with \`{ "session_id": "<id>" }\`. Default \`timeout_ms\` is short (~30s); for audits, refactors, or test runs use a **large** \`timeout_ms\` (e.g. \`600000\`) or poll with \`list_agents\` if appropriate.
8733
+ - \`list_agents\` accepts optional \`parent_session_id\` and \`status\` filters.
8734
+ - **Parallelism:** In a single turn you may issue **multiple** \`spawn_agent\` calls back-to-back so independent workers start together; then \`wait_agent\` on each \`session_id\` (order as needed).
8735
+ - You do **not** receive XML or push notifications from workers \u2014 completion and payload come from \`wait_agent\` (and \`result\` inside that JSON when present).
8736
+
8737
+ ### When to call \`spawn_agent\`:
8738
+
8739
+ - **Research**: "Investigate the authentication structure"
8740
+ - **Implementation**: "Implement feature X in file Y"
8741
+ - **Verification**: "Run tests and verify they pass"
8742
+ - **Refactoring**: "Refactor module Z to use modern patterns"
8743
+
8744
+ **Concurrency Tip**: Launch **independent workers in parallel** whenever possible.
8745
+
8746
+ ## 3. Task Workflow
8747
+
8748
+ ### Phases
8749
+
8750
+ | Phase | Who | Purpose |
8751
+ |-------|-----|---------|
8752
+ | **Research** | Workers (parallel) | Investigate codebase, find files, understand problem |
8753
+ | **Synthesis** | **You** (coordinator) | Read findings, understand problem, craft implementation specs |
8754
+ | **Implementation** | Workers | Make targeted changes per spec, commit |
8755
+ | **Verification** | Workers | Test changes work |
8756
+
8757
+ ### Concurrency is Your Superpower
8758
+
8759
+ **Workers are async.** Launch independent tasks **concurrently** - don't serialize work that can run in parallel.
8760
+
8761
+ **Manage concurrency:**
8762
+ - Read-only tasks (research) - run in parallel freely
8763
+ - Write-heavy tasks (implementation) - one at a time per file set
8764
+ - Verification can run alongside implementation on different file areas
8765
+
8766
+ ## 4. Writing Worker Prompts
8767
+
8768
+ **Workers CANNOT see your conversation.** Every prompt must be **self-contained** with everything the worker needs.
8769
+
8770
+ ### ALWAYS Synthesize - Your Most Important Job
8771
+
8772
+ When workers report research findings, **you must understand them before directing follow-up**.
8773
+
8774
+ **NEVER write:**
8775
+ - "Based on your findings, implement the fix"
8776
+ - "The worker found an issue in the auth module. Please fix it."
8777
+
8778
+ **ALWAYS write:**
8779
+ - "Fix the null pointer in src/auth/validate.ts:42. The user field is undefined when Session.expired is true but the token is still cached. Add a null check before accessing user.id - if null, return 401 with 'Session expired'. Commit and report the hash."
8780
+
8781
+ ### Add a Purpose Statement
8782
+
8783
+ Include a brief purpose so workers can calibrate depth:
8784
+ - "This research will inform a PR description - focus on user-facing changes."
8785
+ - "I need this to plan an implementation - report file paths, line numbers, and type signatures."
8786
+ - "This is a quick check before we merge - just verify the happy path."
8787
+
8788
+ ### Continue vs Spawn Fresh
8789
+
8790
+ | Situation | Mechanism | Why |
8791
+ |-----------|-----------|-----|
8792
+ | Research explored exactly the files that need editing | **Continue** (wait + new prompt) | Worker already has files in context + now gets a clear plan |
8793
+ | Research was broad but implementation is narrow | **Spawn fresh** | Avoid dragging along exploration noise; focused context is cleaner |
8794
+ | Correcting a failure or extending recent work | **Continue** | Worker has the error context and knows what it just tried |
8795
+ | Verifying code a different worker just wrote | **Spawn fresh** | Verifier should see the code with fresh eyes |
8796
+ | First implementation used the wrong approach entirely | **Spawn fresh** | Wrong-approach context pollutes the retry |
8797
+ | Completely unrelated task | **Spawn fresh** | No useful context to reuse |
8798
+
8799
+ **There is no universal default.** Think about how much of the worker's context overlaps with the next task.
8800
+
8801
+ ### Prompt Examples
8802
+
8803
+ **Good examples:**
8804
+
8805
+ 1. **Implementation**: "Fix the null pointer in src/auth/validate.ts:42. The user field can be undefined when the session expires. Add a null check and return early with an appropriate error. Commit and report the hash."
8806
+
8807
+ 2. **Precise git operation**: "Create a new branch from main called 'fix/session-expiry'. Cherry-pick only commit abc123 onto it. Push and create a draft PR targeting main. Add reviewers. Report the PR URL."
8808
+
8809
+ 3. **Correction (continued worker, short)**: "Two tests still failing at lines 58 and 72 - update the assertions to match the new error message."
8810
+
8811
+ **Bad examples:**
8812
+
8813
+ 1. "Fix the bug we discussed" - no context, workers can't see your conversation
8814
+ 2. "Based on your findings, implement" - lazy delegation; synthesize the findings yourself
8815
+ 3. "Create a PR for the recent changes" - ambiguous scope: which changes? which branch?
8816
+ 4. "Something went wrong with the tests, can you look?" - no error message, no file path, no direction
8817
+
8818
+ ## 5. What Real Verification Looks Like
8819
+
8820
+ Verification means **proving the code works**, not confirming it exists.
8821
+
8822
+ - Run tests **with the feature enabled** - not just "tests pass"
8823
+ - Run typechecks and **investigate errors** - don't dismiss as "unrelated"
8824
+ - Be skeptical - if something looks off, dig in
8825
+ - **Test independently** - prove the change works, don't rubber-stamp
8826
+
8827
+ ## 6. Example Session
8828
+
8829
+ **User**: "There's a null pointer in the auth module. Can you fix it?"
8830
+
8831
+ **You (Coordinator)**:
8832
+ \`\`\`
8833
+ Let me investigate first.
8834
+
8835
+ spawn_agent({
8836
+ task: "Investigate the auth module in src/auth/. Find where null pointer exceptions could occur around session handling and token validation. Report specific file paths, line numbers, and types involved. Do NOT modify files.",
8837
+ title: "Research: Auth Bug Investigation",
8838
+ agent_type: "researcher"
8839
+ })
8840
+
8841
+ spawn_agent({
8842
+ task: "Find all test files related to src/auth/. Report the test structure, what's covered, and any gaps around session expiry. Do NOT modify files.",
8843
+ title: "Research: Auth Test Coverage",
8844
+ agent_type: "researcher"
8845
+ })
8846
+
8847
+ Investigating from two angles - I'll report back with findings.
8848
+ \`\`\`
8849
+
8850
+ **You** call \`wait_agent({ session_id: "<id-from-spawn>", timeout_ms: 600000 })\` for each research worker. Tool result shape is JSON, e.g. \`completed: true\`, \`session\` summary, and \`result\` with the worker's last structured output \u2014 **synthesize from that**, not from a fictional XML envelope.
8851
+
8852
+ **After workers finish**, you have concrete paths and line numbers from their \`result\` / messages.
8853
+
8854
+ **You (Coordinator)**:
8855
+ \`\`\`
8856
+ Found the bug - null pointer in validate.ts:42.
8857
+
8858
+ spawn_agent({
8859
+ task: "Fix the null pointer in src/auth/validate.ts:42. Add a null check before accessing user.id - if null, return 401 with 'Session expired'. Run relevant tests and typecheck. Commit and report the hash.",
8860
+ title: "Fix: Auth Null Pointer",
8861
+ agent_type: "implementer"
8862
+ })
8863
+
8864
+ Fix in progress.
8865
+ \`\`\`
8866
+
8867
+ **User**: "How's it going?"
8868
+
8869
+ **You**:
8870
+ \`\`\`
8871
+ Fix for the null pointer in progress. Waiting for worker to complete tests and commit.
8872
+ \`\`\`
8873
+
8874
+ ## 7. Final Tips
8875
+
8876
+ ### Parallelism Tips
8877
+ - Launch 2-4 research workers in parallel
8878
+ - Group implementations by file/module
8879
+ - Verification can be parallel if testing different modules
8880
+
8881
+ ### Communication Tips
8882
+ - Always tell the user what you launched
8883
+ - Don't fabricate or predict worker results
8884
+ - Summarize new information as it arrives from workers
8885
+ - Be transparent about progress
8886
+
8887
+ ### Quality Tips
8888
+ - Synthesis > lazy delegation
8889
+ - Specific > vague
8890
+ - File paths + line numbers > "in module X"
8891
+ - "Prove it works" > "Confirm it exists"
8892
+
8893
+ ---
8894
+
8895
+ **Remember**: You are a **Coordinator**. Your value is in **intelligent orchestration** and **synthesis**; delegate implementation and deep exploration to workers whenever that reduces risk or speeds parallel work.
8896
+ `;
8897
+ function getCoordinatorSystemPrompt() {
8898
+ return COORDINATOR_SYSTEM_PROMPT;
8899
+ }
8900
+ function isCoordinatorModeEnabled() {
8901
+ return process.env.BLUMA_COORDINATOR_MODE === "true";
8902
+ }
8903
+ function enableCoordinatorMode() {
8904
+ process.env.BLUMA_COORDINATOR_MODE = "true";
8905
+ }
8906
+ function disableCoordinatorMode() {
8907
+ delete process.env.BLUMA_COORDINATOR_MODE;
8908
+ }
8909
+
8638
8910
  // src/app/agent/core/prompt/prompt_builder.ts
8639
8911
  function getNodeVersion() {
8640
8912
  try {
@@ -8769,9 +9041,12 @@ function buildCoordinatorModePrompt(mode) {
8769
9041
  if (mode !== "coordinator") {
8770
9042
  return "";
8771
9043
  }
9044
+ const playbook = getCoordinatorSystemPrompt().trim();
8772
9045
  return `
8773
9046
  <coordinator_mode>
8774
- **Coordinator mode** is active: prefer delegating bounded work to \`spawn_agent\` with a crisp objective and inputs, then \`wait_agent\` / \`list_agents\` to collect results. Use \`message\` (\`info\`) to narrate coordination. Avoid direct \`edit_tool\` / \`file_write\` when a subagent can own the implementation; do shallow delegation (do not assume nested hidden workers). Ground every summary in actual tool outputs.
9047
+ **Runtime: coordinator mode is active.** The following playbook is binding.
9048
+
9049
+ ${playbook}
8775
9050
  </coordinator_mode>
8776
9051
  `;
8777
9052
  }
@@ -10114,6 +10389,14 @@ var BluMaAgent = class {
10114
10389
  turnId,
10115
10390
  sessionId: userContextInput.sessionId || this.sessionId
10116
10391
  };
10392
+ process.env.BLUMA_USER_CONTEXT_JSON = JSON.stringify({
10393
+ conversationId: this.activeTurnContext.conversationId ?? null,
10394
+ userId: this.activeTurnContext.userId ?? null,
10395
+ userName: this.activeTurnContext.userName ?? null,
10396
+ userEmail: this.activeTurnContext.userEmail ?? null,
10397
+ companyId: this.activeTurnContext.companyId ?? null,
10398
+ companyName: this.activeTurnContext.companyName ?? null
10399
+ });
10117
10400
  const userContent = buildUserMessageContent(inputText, process.cwd());
10118
10401
  this.history.push({ role: "user", content: userContent });
10119
10402
  this.emptyAssistantReplySteps = 0;
@@ -11475,8 +11758,67 @@ var Agent = class {
11475
11758
  if (inputText === "/init" || inputText.startsWith("/init ")) {
11476
11759
  this.routeManager.registerRoute("/init", this.dispatchToSubAgent);
11477
11760
  }
11761
+ if (inputText === "/coordinator" || inputText.startsWith("/coordinator ")) {
11762
+ this.routeManager.registerRoute("/coordinator", this.handleCoordinatorCommand.bind(this));
11763
+ }
11478
11764
  await this.routeManager.handleRoute({ content: inputText, userContext: resolvedUserContext });
11479
11765
  }
11766
+ /**
11767
+ * Handler para o comando /coordinator
11768
+ * /coordinator enable - Ativa o Coordinator Mode
11769
+ * /coordinator disable - Desativa o Coordinator Mode
11770
+ * /coordinator status - Mostra status atual
11771
+ * /coordinator - Toggle (ativa se desativado, desativa se ativado)
11772
+ */
11773
+ async handleCoordinatorCommand(payload) {
11774
+ const inputText = payload.content.trim();
11775
+ const args = inputText.split(/\s+/).slice(1);
11776
+ const subcommand = args[0]?.toLowerCase();
11777
+ let message2;
11778
+ let messageType = "info";
11779
+ switch (subcommand) {
11780
+ case "enable":
11781
+ enableCoordinatorMode();
11782
+ message2 = "\u2705 Coordinator Mode ATIVADO!\n\nO BluMa agora atuar\xE1 como Coordinator - orquestrando workers em vez de executar diretamente.\n\nUse spawn_agent() para delegar tarefas, wait_agent() para aguardar resultados, e list_agents() para gerenciar workers.";
11783
+ messageType = "success";
11784
+ break;
11785
+ case "disable":
11786
+ disableCoordinatorMode();
11787
+ message2 = "\u23F9\uFE0F Coordinator Mode DESATIVADO.\n\nO BluMa voltar\xE1 ao modo padr\xE3o de execu\xE7\xE3o direta.";
11788
+ messageType = "info";
11789
+ break;
11790
+ case "status":
11791
+ case "info":
11792
+ const status = isCoordinatorModeEnabled() ? "\u2705 ATIVO" : "\u274C INATIVO";
11793
+ message2 = `\u{1F4CA} Coordinator Mode Status: ${status}
11794
+
11795
+ Quando ativo, o BluMa atua como Coordinator - orquestrando workers para:
11796
+ \u2022 Pesquisa paralela (m\xFAltiplos workers investigando diferentes aspectos)
11797
+ \u2022 Implementa\xE7\xE3o delegada (workers especializados por m\xF3dulo)
11798
+ \u2022 Verifica\xE7\xE3o independente (workers testam mudan\xE7as de outros workers)
11799
+
11800
+ Tools principais:
11801
+ \u2022 spawn_agent() - Criar worker
11802
+ \u2022 wait_agent() - Aguardar conclus\xE3o
11803
+ \u2022 list_agents() - Listar workers ativos`;
11804
+ break;
11805
+ default:
11806
+ if (isCoordinatorModeEnabled()) {
11807
+ disableCoordinatorMode();
11808
+ message2 = "\u23F9\uFE0F Coordinator Mode DESATIVADO (toggle).";
11809
+ messageType = "info";
11810
+ } else {
11811
+ enableCoordinatorMode();
11812
+ message2 = '\u2705 Coordinator Mode ATIVADO!\n\nOrquestrando workers em vez de executar diretamente.\nDica: Use "help" para ver exemplos de como usar spawn_agent().';
11813
+ messageType = "success";
11814
+ }
11815
+ }
11816
+ this.eventBus.emit("backend_message", {
11817
+ type: messageType,
11818
+ code: "coordinator_mode",
11819
+ message: message2
11820
+ });
11821
+ }
11480
11822
  async handleToolResponse(decisionData) {
11481
11823
  await this.core.handleToolResponse(decisionData);
11482
11824
  }
@@ -13543,6 +13885,32 @@ var SlashCommands = ({
13543
13885
  if (cmd === "sessions") {
13544
13886
  return showSessions();
13545
13887
  }
13888
+ if (cmd === "agents") {
13889
+ const entries = listSessions().filter((e) => e.kind === "agent");
13890
+ if (entries.length === 0) {
13891
+ return outBox(
13892
+ /* @__PURE__ */ jsxs15(Fragment6, { children: [
13893
+ /* @__PURE__ */ jsx17(Text15, { bold: true, color: BLUMA_TERMINAL.claude, children: "Worker agents" }),
13894
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " No agent sessions in this CLI registry. Use the " }),
13895
+ /* @__PURE__ */ jsx17(Text15, { bold: true, children: "spawn_agent" }),
13896
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " tool from chat, or " }),
13897
+ /* @__PURE__ */ jsx17(Text15, { bold: true, children: "/agent coordinator" }),
13898
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " then ask the model to delegate." })
13899
+ ] })
13900
+ );
13901
+ }
13902
+ return outBox(
13903
+ /* @__PURE__ */ jsxs15(Fragment6, { children: [
13904
+ /* @__PURE__ */ jsx17(Text15, { bold: true, color: BLUMA_TERMINAL.claude, children: "Worker agents" }),
13905
+ /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
13906
+ " ",
13907
+ entries.length,
13908
+ " session(s)"
13909
+ ] }),
13910
+ /* @__PURE__ */ jsx17(Box16, { marginTop: 1, flexDirection: "column", children: entries.map((e) => /* @__PURE__ */ jsx17(Text15, { dimColor: true, wrap: "wrap", children: formatSessionLine(e) }, e.sessionId)) })
13911
+ ] })
13912
+ );
13913
+ }
13546
13914
  if (cmd === "bridge") {
13547
13915
  return renderBridgePanel();
13548
13916
  }
@@ -13624,7 +13992,9 @@ var SlashCommands = ({
13624
13992
  const cfg = getRuntimeConfig();
13625
13993
  return usageBox(
13626
13994
  "Agent mode",
13627
- `current: ${cfg.agentMode} \xB7 set: /agent default | /agent coordinator`
13995
+ `current: ${cfg.agentMode}
13996
+ set: /agent default | /agent coordinator
13997
+ (list workers: /agents \u2014 not the same as /agent)`
13628
13998
  );
13629
13999
  }
13630
14000
  if (cmd === "features") {
@@ -14455,7 +14825,20 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
14455
14825
  }, [isProcessing, eventBus]);
14456
14826
  const handleSubmit = useCallback3(
14457
14827
  (text) => {
14458
- if (!text || isProcessing || !agentInstance.current) return;
14828
+ if (!text || !agentInstance.current) return;
14829
+ const trimmedForSlash = text.trim();
14830
+ if (isProcessing && !isSlashRoutingLine(trimmedForSlash)) {
14831
+ if (trimmedForSlash.startsWith("/")) {
14832
+ setHistory((prev) => [
14833
+ ...prev,
14834
+ {
14835
+ id: prev.length,
14836
+ component: /* @__PURE__ */ jsx24(ChatMeta, { children: "Slash command not recognized or incomplete. Type /help for the list." })
14837
+ }
14838
+ ]);
14839
+ }
14840
+ return;
14841
+ }
14459
14842
  lastReasoningTextRef.current = null;
14460
14843
  lastStreamAssistantKeyRef.current = null;
14461
14844
  if (/^\/img\s+/i.test(text) || /^\/image\s+/i.test(text)) {
@@ -14896,11 +15279,21 @@ Please use command_status to check the result and report back to the user.`;
14896
15279
  const handleUiOverlay = (data) => {
14897
15280
  eventBus.emit("user_overlay", data);
14898
15281
  };
15282
+ const handleInputNotice = (data) => {
15283
+ const msg = String(data.message || "").trim();
15284
+ if (!msg) return;
15285
+ setHistory((prev) => [
15286
+ ...prev,
15287
+ { id: prev.length, component: /* @__PURE__ */ jsx24(ChatMeta, { children: msg }) }
15288
+ ]);
15289
+ };
14899
15290
  uiEventBus.on("user_overlay", handleUiOverlay);
15291
+ uiEventBus.on("input_notice", handleInputNotice);
14900
15292
  eventBus.on("backend_message", handleBackendMessage);
14901
15293
  initializeAgent();
14902
15294
  return () => {
14903
15295
  uiEventBus.off("user_overlay", handleUiOverlay);
15296
+ uiEventBus.off("input_notice", handleInputNotice);
14904
15297
  eventBus.off("backend_message", handleBackendMessage);
14905
15298
  };
14906
15299
  }, [eventBus, sessionId, handleConfirmation]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomad-e/bluma-cli",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "description": "BluMa independent agent for automation and advanced software engineering.",
5
5
  "author": "Alex Fonseca",
6
6
  "license": "Apache-2.0",