@bubblebrain-ai/bubble 0.0.2 → 0.0.3

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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * Main entry point - assembles all layers and runs the agent.
4
4
  */
package/dist/main.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * Main entry point - assembles all layers and runs the agent.
4
4
  */
package/dist/tui/run.js CHANGED
@@ -387,6 +387,10 @@ function OpenTuiApp(props) {
387
387
  const sidebarLspRows = [];
388
388
  const sidebarLspMarkers = [];
389
389
  const sidebarLspLabels = [];
390
+ let sidebarTodoSection;
391
+ const sidebarTodoRows = [];
392
+ const sidebarTodoMarkers = [];
393
+ const sidebarTodoLabels = [];
390
394
  const sidebarFileRows = [];
391
395
  const sidebarFileLabels = [];
392
396
  const sidebarFileAdditions = [];
@@ -621,6 +625,12 @@ function OpenTuiApp(props) {
621
625
  setSidebarTick((value) => value + 1);
622
626
  syncSidebarContext();
623
627
  };
628
+ const syncTodosFromAgent = () => {
629
+ const nextTodos = props.agent.getTodos();
630
+ setTodos(nextTodos);
631
+ syncSidebarTodos(nextTodos);
632
+ bumpSidebar();
633
+ };
624
634
  function refreshGitSidebar() {
625
635
  setGitState(readGitSidebarState(props.args.cwd));
626
636
  syncSidebarFiles();
@@ -721,6 +731,35 @@ function OpenTuiApp(props) {
721
731
  }
722
732
  sidebarShell?.requestRender();
723
733
  }
734
+ function syncSidebarTodos(nextTodos = todos()) {
735
+ const visible = nextTodos.slice(0, 8);
736
+ if (sidebarTodoSection) {
737
+ sidebarTodoSection.visible = visible.length > 0;
738
+ }
739
+ for (let index = 0; index < 8; index++) {
740
+ const row = sidebarTodoRows[index];
741
+ const marker = sidebarTodoMarkers[index];
742
+ const label = sidebarTodoLabels[index];
743
+ const todo = visible[index];
744
+ if (!row || !marker || !label)
745
+ continue;
746
+ row.visible = !!todo;
747
+ if (!todo) {
748
+ safeRequestRender(row);
749
+ continue;
750
+ }
751
+ const completed = todo.status === "completed";
752
+ const inProgress = todo.status === "in_progress";
753
+ const labelText = inProgress ? (todo.activeForm || todo.content) : todo.content;
754
+ marker.content = completed ? "✓" : inProgress ? "◉" : "○";
755
+ marker.fg = completed ? theme.success : inProgress ? theme.warning : theme.textMuted;
756
+ label.content = labelText;
757
+ label.fg = completed ? theme.success : inProgress ? theme.warning : theme.textMuted;
758
+ safeRequestRender(row);
759
+ }
760
+ sidebarShell?.requestRender();
761
+ rootBox?.requestRender();
762
+ }
724
763
  function showSidebarLspRows(statuses) {
725
764
  for (let index = 0; index < sidebarLspRows.length; index++) {
726
765
  const row = sidebarLspRows[index];
@@ -3016,6 +3055,7 @@ function OpenTuiApp(props) {
3016
3055
  return true;
3017
3056
  if (props.agent.mode !== mode())
3018
3057
  setMode(props.agent.mode);
3058
+ syncTodosFromAgent();
3019
3059
  syncModelChrome();
3020
3060
  syncModeChrome();
3021
3061
  if (uiDisposed)
@@ -3494,6 +3534,7 @@ function OpenTuiApp(props) {
3494
3534
  }
3495
3535
  else if (event.type === "todos_updated") {
3496
3536
  setTodos(event.todos);
3537
+ syncSidebarTodos(event.todos);
3497
3538
  bumpSidebar();
3498
3539
  }
3499
3540
  else if (event.type === "mode_changed") {
@@ -4068,7 +4109,6 @@ function OpenTuiApp(props) {
4068
4109
  const context = sidebarContextState();
4069
4110
  const mcpStates = sidebarMcpStates();
4070
4111
  const files = gitState().files;
4071
- const activeTodos = todos().filter((todo) => todo.status !== "completed");
4072
4112
  return h("box", {
4073
4113
  ref: (ref) => {
4074
4114
  sidebarShell = ref;
@@ -4154,7 +4194,7 @@ function OpenTuiApp(props) {
4154
4194
  ? renderSidebarSection("Compactions", [
4155
4195
  h("text", { fg: theme.info, wrapMode: "word" }, `${currentTranscriptMessages().filter((m) => m.syntheticKind === "ui_compact_card").length} in this session`),
4156
4196
  ])
4157
- : null, renderSidebarMcp(mcpStates), renderSidebarLsp(), activeTodos.length ? renderSidebarTodos(activeTodos) : null, renderSidebarFiles(files))),
4197
+ : null, renderSidebarMcp(mcpStates), renderSidebarLsp(), renderSidebarTodos(todos()), renderSidebarFiles(files))),
4158
4198
  renderSidebarFooter(),
4159
4199
  ]);
4160
4200
  }
@@ -4239,11 +4279,37 @@ function OpenTuiApp(props) {
4239
4279
  }),
4240
4280
  ]);
4241
4281
  }
4242
- function renderSidebarTodos(activeTodos) {
4243
- return renderSidebarSection("Todo", activeTodos.slice(0, 6).map((todo) => {
4244
- const marker = todo.status === "in_progress" ? ">" : "o";
4245
- const color = todo.status === "in_progress" ? theme.primary : theme.textMuted;
4246
- return h("text", { fg: color, wrapMode: "word" }, `${marker} ${todo.activeForm || todo.content}`);
4282
+ function renderSidebarTodos(todos) {
4283
+ const visible = todos.slice(0, 8);
4284
+ return h("box", {
4285
+ flexDirection: "column",
4286
+ flexShrink: 0,
4287
+ visible: visible.length > 0,
4288
+ ref: (ref) => {
4289
+ sidebarTodoSection = ref;
4290
+ syncSidebarTodos();
4291
+ },
4292
+ }, h("text", { fg: theme.text }, "Todo"), ...Array.from({ length: 8 }, (_, index) => {
4293
+ const todo = visible[index];
4294
+ const completed = todo?.status === "completed";
4295
+ const inProgress = todo?.status === "in_progress";
4296
+ const labelText = todo
4297
+ ? (inProgress ? (todo.activeForm || todo.content) : todo.content)
4298
+ : "";
4299
+ return h("box", {
4300
+ flexDirection: "row",
4301
+ gap: 1,
4302
+ visible: !!todo,
4303
+ ref: (ref) => { sidebarTodoRows[index] = ref; },
4304
+ }, h("text", {
4305
+ fg: completed ? theme.success : inProgress ? theme.warning : theme.textMuted,
4306
+ flexShrink: 0,
4307
+ ref: (ref) => { sidebarTodoMarkers[index] = ref; },
4308
+ }, completed ? "✓" : inProgress ? "◉" : "○"), h("text", {
4309
+ fg: completed ? theme.success : inProgress ? theme.warning : theme.textMuted,
4310
+ wrapMode: "word",
4311
+ ref: (ref) => { sidebarTodoLabels[index] = ref; },
4312
+ }, labelText));
4247
4313
  }));
4248
4314
  }
4249
4315
  function renderSidebarFiles(files) {
@@ -5249,10 +5315,50 @@ function createModelSwitchEntry(ctx, model, key, signature) {
5249
5315
  ]);
5250
5316
  return { key, signature, node, refs: {} };
5251
5317
  }
5318
+ function createTodoWriteRenderable(ctx, tool) {
5319
+ const todos = tool.args.todos || [];
5320
+ const summary = tool.result || "";
5321
+ if (!isToolFinished(tool)) {
5322
+ return createBox(ctx, {
5323
+ paddingLeft: 3,
5324
+ marginTop: 1,
5325
+ flexDirection: "column",
5326
+ flexShrink: 0,
5327
+ }, [
5328
+ createText(ctx, `~ → Planning tasks...`, { fg: toolColor(tool) }),
5329
+ ]);
5330
+ }
5331
+ return createBox(ctx, {
5332
+ border: ["left"],
5333
+ borderColor: theme.borderSubtle,
5334
+ backgroundColor: theme.backgroundPanel,
5335
+ marginTop: 1,
5336
+ paddingTop: 1,
5337
+ paddingBottom: 1,
5338
+ paddingLeft: 2,
5339
+ flexDirection: "column",
5340
+ flexShrink: 0,
5341
+ }, [
5342
+ createText(ctx, `# Todo ${summary ? `— ${summary}` : ""}`, { fg: theme.textMuted }),
5343
+ ...todos.map((todo, index) => {
5344
+ const completed = todo.status === "completed";
5345
+ const inProgress = todo.status === "in_progress";
5346
+ const marker = completed ? "✓" : inProgress ? "◉" : "○";
5347
+ const fg = completed ? theme.success : inProgress ? theme.warning : theme.textMuted;
5348
+ return createText(ctx, ` ${marker} ${todo.content}`, {
5349
+ fg,
5350
+ marginTop: index === 0 ? 1 : 0,
5351
+ });
5352
+ }),
5353
+ ]);
5354
+ }
5252
5355
  function createToolRenderable(ctx, tool, syntaxStyle, width = 80) {
5253
5356
  if (tool.name === "question") {
5254
5357
  return createQuestionToolRenderable(ctx, tool);
5255
5358
  }
5359
+ if (tool.name === "todo_write") {
5360
+ return createTodoWriteRenderable(ctx, tool);
5361
+ }
5256
5362
  const icon = tool.name === "bash" ? "$" : tool.name === "edit" || tool.name === "write" ? "✎" : "●";
5257
5363
  const color = toolColor(tool);
5258
5364
  const header = toolHeader(tool);
@@ -5988,8 +6094,10 @@ function extractToolDiff(tool) {
5988
6094
  const index = tool.result.indexOf(marker);
5989
6095
  if (index === -1)
5990
6096
  return undefined;
5991
- const diff = tool.result.slice(index + marker.length).trim();
5992
- return diff ? diff : undefined;
6097
+ const rawDiff = tool.result.slice(index + marker.length);
6098
+ const diagnosticsIndex = rawDiff.search(/\n\nLSP diagnostics in /);
6099
+ const diff = diagnosticsIndex === -1 ? rawDiff : rawDiff.slice(0, diagnosticsIndex);
6100
+ return diff.trim().length > 0 ? diff : undefined;
5993
6101
  }
5994
6102
  function diffViewMode(width = 80) {
5995
6103
  return width > 120 ? "split" : "unified";
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@bubblebrain-ai/bubble",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "A terminal coding agent",
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": ">=20.0.0"
8
+ },
6
9
  "bin": {
7
10
  "bubble": "dist/main.js"
8
11
  },