From 30832cb7cdaf32e7c49c5a6c4a6c1b3aebb5ad1c Mon Sep 17 00:00:00 2001 From: m-aebrer Date: Fri, 15 May 2026 13:35:47 -0400 Subject: [PATCH 1/6] chore: open PR for issue 210 From b7c03c73744e98bfe81605a2bdef235f8de906e8 Mon Sep 17 00:00:00 2001 From: m-aebrer Date: Fri, 15 May 2026 13:40:40 -0400 Subject: [PATCH 2/6] feat(suggest-next): end turn automatically and add summary parameter - Add endTurn: true to success return path so agent loop stops after suggest_next (saves one LLM round-trip per suggestion) - Error path (invalid command) does NOT set endTurn, allowing model retry - Add optional summary parameter for agent to provide a markdown wrap-up of work done, rendered above the suggestion arrow - Update promptGuidelines to instruct models about summary and turn ending - Add tests for endTurn behavior and summary parameter --- .../src/core/tools/suggest-next.ts | 21 ++++- .../coding-agent/test/suggest-next.test.ts | 81 ++++++++++++++++++- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/packages/coding-agent/src/core/tools/suggest-next.ts b/packages/coding-agent/src/core/tools/suggest-next.ts index 9197ee8..437cbf3 100644 --- a/packages/coding-agent/src/core/tools/suggest-next.ts +++ b/packages/coding-agent/src/core/tools/suggest-next.ts @@ -14,6 +14,7 @@ import type { ToolDefinition } from "../extensions/types.js"; export interface SuggestNextDetails { suggestion: string; + summary?: string; } export type SuggestNextCallback = (suggestion: string) => void; @@ -25,6 +26,12 @@ const suggestNextSchema = Type.Object({ command: Type.String({ description: "The suggested command for the user to run next (e.g. /skill:mach6-push, /compact)", }), + summary: Type.Optional( + Type.String({ + description: + "Brief markdown summary of the work done this turn. Displayed to the user as the final message before the suggestion.", + }), + ), }); export type SuggestNextInput = Static; @@ -46,7 +53,13 @@ function formatSuggestNextResult( const text = result.content?.[0]; return text?.type === "text" && text.text ? theme.fg("toolOutput", text.text) : ""; } - return theme.fg("toolOutput", `→ ${details.suggestion}`); + const lines: string[] = []; + if (details.summary) { + lines.push(details.summary); + lines.push(""); + } + lines.push(theme.fg("toolOutput", `→ ${details.suggestion}`)); + return lines.join("\n"); } // ============================================================================ @@ -70,9 +83,10 @@ export function createSuggestNextToolDefinition( "Use full command syntax: /skill:name args, /compact, etc.", "Only suggest one command — pick the most likely next step", "Don't suggest if the conversation is open-ended with no obvious next action", + "Include a brief summary of work done in the `summary` parameter — this is your last chance to communicate before the turn ends", ], - async execute(_toolCallId, { command: rawCommand }: SuggestNextInput, _signal?, _onUpdate?, _ctx?) { + async execute(_toolCallId, { command: rawCommand, summary }: SuggestNextInput, _signal?, _onUpdate?, _ctx?) { // Strip control characters (newlines, tabs, etc.) that would corrupt TUI rendering const command = rawCommand?.replace(/[\x00-\x1f\x7f]/g, "").trim(); if (!command || !command.startsWith("/")) { @@ -86,7 +100,8 @@ export function createSuggestNextToolDefinition( return { content: [{ type: "text" as const, text: `Suggestion registered: ${command}` }], - details: { suggestion: command }, + details: { suggestion: command, summary: summary?.trim() || undefined }, + endTurn: true, }; }, diff --git a/packages/coding-agent/test/suggest-next.test.ts b/packages/coding-agent/test/suggest-next.test.ts index 59180e0..4e188bf 100644 --- a/packages/coding-agent/test/suggest-next.test.ts +++ b/packages/coding-agent/test/suggest-next.test.ts @@ -8,10 +8,14 @@ describe("suggest_next tool", () => { // Cast execute to skip the ctx parameter (not used by this tool) const execute = tool.execute.bind(tool) as ( toolCallId: string, - params: { command: string }, + params: { command: string; summary?: string }, signal?: AbortSignal, onUpdate?: any, - ) => Promise<{ content: Array<{ type: string; text?: string }>; details?: SuggestNextDetails }>; + ) => Promise<{ + content: Array<{ type: string; text?: string }>; + details?: SuggestNextDetails; + endTurn?: boolean; + }>; return { execute, onSuggest }; } @@ -57,6 +61,79 @@ describe("suggest_next tool", () => { expect(onSuggest).toHaveBeenCalledWith("/skill:mach6-plan 201"); }); + describe("endTurn behavior", () => { + it("sets endTurn: true on successful execution", async () => { + const { execute } = createTool(); + + const result = await execute("call-et-1", { command: "/compact" }); + + expect(result.endTurn).toBe(true); + }); + + it("does not set endTurn on error (invalid command)", async () => { + const { execute } = createTool(); + + const result = await execute("call-et-2", { command: "npm run build" }); + + expect(result.endTurn).toBeUndefined(); + }); + + it("does not set endTurn on error (empty command)", async () => { + const { execute } = createTool(); + + const result = await execute("call-et-3", { command: "" }); + + expect(result.endTurn).toBeUndefined(); + }); + }); + + describe("summary parameter", () => { + it("includes summary in details when provided", async () => { + const { execute } = createTool(); + + const result = await execute("call-s-1", { + command: "/skill:mach6-push", + summary: "Updated the auth handler and added tests.", + }); + + expect(result.details).toEqual({ + suggestion: "/skill:mach6-push", + summary: "Updated the auth handler and added tests.", + }); + }); + + it("works without summary (backward compat)", async () => { + const { execute } = createTool(); + + const result = await execute("call-s-2", { command: "/compact" }); + + expect(result.details).toEqual({ suggestion: "/compact" }); + expect(result.details!.summary).toBeUndefined(); + }); + + it("trims whitespace from summary", async () => { + const { execute } = createTool(); + + const result = await execute("call-s-3", { + command: "/compact", + summary: " Done with the refactor. ", + }); + + expect(result.details!.summary).toBe("Done with the refactor."); + }); + + it("treats empty summary as undefined", async () => { + const { execute } = createTool(); + + const result = await execute("call-s-4", { + command: "/compact", + summary: " ", + }); + + expect(result.details!.summary).toBeUndefined(); + }); + }); + describe("control character sanitization", () => { it("strips control characters and accepts valid command", async () => { const { execute, onSuggest } = createTool(); From d117c8dfb35201a969bda2e46a0a3b6b3cd99092 Mon Sep 17 00:00:00 2001 From: m-aebrer Date: Fri, 15 May 2026 14:12:56 -0400 Subject: [PATCH 3/6] fix(suggest-next): render summary as markdown and sanitize control characters --- .../src/core/tools/suggest-next.ts | 45 ++++++++++--------- .../coding-agent/test/suggest-next.test.ts | 22 +++++++++ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/packages/coding-agent/src/core/tools/suggest-next.ts b/packages/coding-agent/src/core/tools/suggest-next.ts index 437cbf3..8384a9c 100644 --- a/packages/coding-agent/src/core/tools/suggest-next.ts +++ b/packages/coding-agent/src/core/tools/suggest-next.ts @@ -5,8 +5,9 @@ * The suggestion is shown as ghost text in the editor prompt (Tab to accept). */ -import { Text } from "@dreb/tui"; +import { Container, Markdown, Text } from "@dreb/tui"; import { type Static, Type } from "@sinclair/typebox"; +import { getMarkdownTheme } from "../../modes/interactive/theme/theme.js"; import type { ToolDefinition } from "../extensions/types.js"; // ============================================================================ @@ -44,24 +45,6 @@ function formatSuggestNextCall(args: { command?: string } | undefined, theme: an return `${theme.fg("toolTitle", theme.bold("suggest_next"))} ${theme.fg("accent", cmd)}`; } -function formatSuggestNextResult( - result: { content: Array<{ type: string; text?: string }>; details?: SuggestNextDetails }, - theme: any, -): string { - const details = result.details; - if (!details) { - const text = result.content?.[0]; - return text?.type === "text" && text.text ? theme.fg("toolOutput", text.text) : ""; - } - const lines: string[] = []; - if (details.summary) { - lines.push(details.summary); - lines.push(""); - } - lines.push(theme.fg("toolOutput", `→ ${details.suggestion}`)); - return lines.join("\n"); -} - // ============================================================================ // Tool definition factory @@ -98,9 +81,12 @@ export function createSuggestNextToolDefinition( onSuggest(command); + // Strip control characters from summary (preserve newlines for markdown) + const sanitizedSummary = summary?.replace(/[\x00-\x09\x0b\x0c\x0e-\x1f\x7f]/g, "").trim() || undefined; + return { content: [{ type: "text" as const, text: `Suggestion registered: ${command}` }], - details: { suggestion: command, summary: summary?.trim() || undefined }, + details: { suggestion: command, summary: sanitizedSummary }, endTurn: true, }; }, @@ -112,8 +98,25 @@ export function createSuggestNextToolDefinition( }, renderResult(result, _options, theme, context) { + const details = (result as any).details as SuggestNextDetails | undefined; + if (!details) { + const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0); + const content = result.content?.[0]; + const msg = content?.type === "text" && content.text ? content.text : ""; + text.setText(theme.fg("toolOutput", msg)); + return text; + } + + if (details.summary) { + const container = (context.lastComponent as Container | undefined) ?? new Container(); + container.clear(); + container.addChild(new Markdown(details.summary, 0, 0, getMarkdownTheme())); + container.addChild(new Text(theme.fg("toolOutput", `→ ${details.suggestion}`), 0, 0)); + return container; + } + const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0); - text.setText(formatSuggestNextResult(result as any, theme)); + text.setText(theme.fg("toolOutput", `→ ${details.suggestion}`)); return text; }, }; diff --git a/packages/coding-agent/test/suggest-next.test.ts b/packages/coding-agent/test/suggest-next.test.ts index 4e188bf..6fe3700 100644 --- a/packages/coding-agent/test/suggest-next.test.ts +++ b/packages/coding-agent/test/suggest-next.test.ts @@ -132,6 +132,28 @@ describe("suggest_next tool", () => { expect(result.details!.summary).toBeUndefined(); }); + + it("strips control characters from summary but preserves newlines", async () => { + const { execute } = createTool(); + + const result = await execute("call-s-5", { + command: "/compact", + summary: "Line one\nLine two\x00\x1b[2J\x07end", + }); + + expect(result.details!.summary).toBe("Line one\nLine two[2Jend"); + }); + + it("treats control-character-only summary as undefined", async () => { + const { execute } = createTool(); + + const result = await execute("call-s-6", { + command: "/compact", + summary: "\x01\x02\x03", + }); + + expect(result.details!.summary).toBeUndefined(); + }); }); describe("control character sanitization", () => { From 12fdd97812d32a6a649d385ee4453c6e96b35484 Mon Sep 17 00:00:00 2001 From: m-aebrer Date: Fri, 15 May 2026 15:36:16 -0400 Subject: [PATCH 4/6] fix(suggest-next): convert literal \n, strip CR, add end-turn guideline, exclude from subagents --- packages/coding-agent/src/core/sdk.ts | 8 +++++-- .../coding-agent/src/core/tools/subagent.ts | 11 +++++---- .../src/core/tools/suggest-next.ts | 10 ++++++-- .../coding-agent/test/suggest-next.test.ts | 23 +++++++++++++++++++ packages/coding-agent/test/tools/wait.test.ts | 6 ++--- 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/packages/coding-agent/src/core/sdk.ts b/packages/coding-agent/src/core/sdk.ts index 11bc7c7..8b3daab 100644 --- a/packages/coding-agent/src/core/sdk.ts +++ b/packages/coding-agent/src/core/sdk.ts @@ -256,8 +256,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} thinkingLevel = "off"; } - // Tools that are always active when available (created by factory, not in allTools singleton) - const alwaysActiveBuiltins = ["skill", "tasks_update", "search", "suggest_next"]; + // Tools that are always active when available (created by factory, not in allTools singleton). + // suggest_next is only auto-activated when tools aren't explicitly specified — subagent + // child processes pass --tools which excludes suggest_next (it would end the turn mid-work). + const alwaysActiveBuiltins = options.tools + ? ["skill", "tasks_update", "search"] + : ["skill", "tasks_update", "search", "suggest_next"]; const defaultActiveToolNames: ToolName[] = [ "read", "bash", diff --git a/packages/coding-agent/src/core/tools/subagent.ts b/packages/coding-agent/src/core/tools/subagent.ts index 39869c4..5940eb4 100644 --- a/packages/coding-agent/src/core/tools/subagent.ts +++ b/packages/coding-agent/src/core/tools/subagent.ts @@ -156,14 +156,15 @@ const DREB_SCRIPT = process.argv[1] || "dreb"; const NODE_EXEC = process.execPath; // Tools that must never be available to subagents — wait (subagents should -// never no-op; they have a task to complete) and subagent (no recursive spawning). -const SUBAGENT_EXCLUDED_TOOLS = ["wait", "subagent"] as const; +// never no-op; they have a task to complete), subagent (no recursive spawning), +// and suggest_next (would end the subagent's turn mid-work). +const SUBAGENT_EXCLUDED_TOOLS = ["wait", "subagent", "suggest_next"] as const; // Default standard tools for subagents when no tools are specified in the agent // definition. This is the set passed via --tools to the child process. // -// NOTE: Always-active tools (search, skill, tasks_update, suggest_next) are NOT -// listed here — the child process adds them unconditionally regardless of --tools. +// NOTE: Always-active tools (search, skill, tasks_update) are NOT listed here — +// the child process adds them unconditionally regardless of --tools. // Internal tools (tmp_read) are also excluded. const SUBAGENT_DEFAULT_TOOLS = ["read", "bash", "edit", "write", "grep", "find", "ls", "web_search", "web_fetch"]; @@ -231,7 +232,7 @@ async function spawnSubagent( args.push("--provider", parentProvider); } } - // Always pass --tools to ensure wait/subagent are excluded from child processes. + // Always pass --tools to ensure wait/subagent/suggest_next are excluded from child processes. // filterSubagentTools always returns a non-empty string. args.push("--tools", filterSubagentTools(agentConfig.tools)); if (agentConfig.systemPrompt) { diff --git a/packages/coding-agent/src/core/tools/suggest-next.ts b/packages/coding-agent/src/core/tools/suggest-next.ts index 8384a9c..c7189e8 100644 --- a/packages/coding-agent/src/core/tools/suggest-next.ts +++ b/packages/coding-agent/src/core/tools/suggest-next.ts @@ -67,6 +67,7 @@ export function createSuggestNextToolDefinition( "Only suggest one command — pick the most likely next step", "Don't suggest if the conversation is open-ended with no obvious next action", "Include a brief summary of work done in the `summary` parameter — this is your last chance to communicate before the turn ends", + "Calling this tool ends your turn automatically — do not call wait afterwards", ], async execute(_toolCallId, { command: rawCommand, summary }: SuggestNextInput, _signal?, _onUpdate?, _ctx?) { @@ -81,8 +82,13 @@ export function createSuggestNextToolDefinition( onSuggest(command); - // Strip control characters from summary (preserve newlines for markdown) - const sanitizedSummary = summary?.replace(/[\x00-\x09\x0b\x0c\x0e-\x1f\x7f]/g, "").trim() || undefined; + // Convert literal \n sequences to actual newlines (LLMs emit these in XML tool calls), + // then strip control characters (preserve only newlines for markdown) + const sanitizedSummary = + summary + ?.replace(/\\n/g, "\n") + .replace(/[\x00-\x09\x0b-\x1f\x7f]/g, "") + .trim() || undefined; return { content: [{ type: "text" as const, text: `Suggestion registered: ${command}` }], diff --git a/packages/coding-agent/test/suggest-next.test.ts b/packages/coding-agent/test/suggest-next.test.ts index 6fe3700..238022f 100644 --- a/packages/coding-agent/test/suggest-next.test.ts +++ b/packages/coding-agent/test/suggest-next.test.ts @@ -144,6 +144,18 @@ describe("suggest_next tool", () => { expect(result.details!.summary).toBe("Line one\nLine two[2Jend"); }); + it("strips carriage return from summary", async () => { + const { execute } = createTool(); + + const result = await execute("call-s-7", { + command: "/compact", + summary: "Line one\r\nLine two\rLine three", + }); + + // \r\n → \n (CR stripped, LF preserved), bare \r → removed + expect(result.details!.summary).toBe("Line one\nLine twoLine three"); + }); + it("treats control-character-only summary as undefined", async () => { const { execute } = createTool(); @@ -154,6 +166,17 @@ describe("suggest_next tool", () => { expect(result.details!.summary).toBeUndefined(); }); + + it("converts literal backslash-n sequences to actual newlines", async () => { + const { execute } = createTool(); + + const result = await execute("call-s-8", { + command: "/compact", + summary: "Line one\\nLine two\\n\\nFinal line", + }); + + expect(result.details!.summary).toBe("Line one\nLine two\n\nFinal line"); + }); }); describe("control character sanitization", () => { diff --git a/packages/coding-agent/test/tools/wait.test.ts b/packages/coding-agent/test/tools/wait.test.ts index 2ecc691..829232a 100644 --- a/packages/coding-agent/test/tools/wait.test.ts +++ b/packages/coding-agent/test/tools/wait.test.ts @@ -199,13 +199,13 @@ describe("filterSubagentTools", () => { expect(result).toBe("read,bash"); }); - it("filters out both wait and subagent", () => { - const result = filterSubagentTools("read,wait,bash,subagent,grep"); + it("filters out wait, subagent, and suggest_next", () => { + const result = filterSubagentTools("read,wait,bash,subagent,suggest_next,grep"); expect(result).toBe("read,bash,grep"); }); it("returns defaults when all tools are excluded", () => { - const result = filterSubagentTools("wait,subagent"); + const result = filterSubagentTools("wait,subagent,suggest_next"); expect(result).toBe("read,bash,edit,write,grep,find,ls,web_search,web_fetch"); }); From 4967bb199618928c426bc795e98591d0f187f6f7 Mon Sep 17 00:00:00 2001 From: m-aebrer Date: Fri, 15 May 2026 16:00:53 -0400 Subject: [PATCH 5/6] test(suggest-next): add renderResult tests covering all 3 branches --- .../coding-agent/test/suggest-next.test.ts | 121 +++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/packages/coding-agent/test/suggest-next.test.ts b/packages/coding-agent/test/suggest-next.test.ts index 238022f..088f205 100644 --- a/packages/coding-agent/test/suggest-next.test.ts +++ b/packages/coding-agent/test/suggest-next.test.ts @@ -1,5 +1,9 @@ -import { describe, expect, it, vi } from "vitest"; +import { Container, Text } from "@dreb/tui"; +import stripAnsi from "strip-ansi"; +import { beforeAll, describe, expect, it, vi } from "vitest"; +import type { ToolRenderContext, ToolRenderResultOptions } from "../src/core/extensions/types.js"; import { createSuggestNextToolDefinition, type SuggestNextDetails } from "../src/core/tools/suggest-next.js"; +import { initTheme, theme } from "../src/modes/interactive/theme/theme.js"; describe("suggest_next tool", () => { function createTool() { @@ -216,4 +220,119 @@ describe("suggest_next tool", () => { expect(result.details).toEqual({ suggestion: "/skill:mach6-plan 42" }); }); }); + + describe("renderResult", () => { + beforeAll(() => { + initTheme("dark"); + }); + + function createRenderContext(overrides: Partial = {}): ToolRenderContext { + return { + args: {}, + toolCallId: "test-call", + invalidate: () => {}, + lastComponent: undefined, + state: {}, + cwd: process.cwd(), + executionStarted: true, + argsComplete: true, + isPartial: false, + expanded: false, + showImages: false, + isError: false, + ...overrides, + }; + } + + const renderOptions: ToolRenderResultOptions = { expanded: false, isPartial: false }; + + it("renders error message when no details present", () => { + const tool = createSuggestNextToolDefinition(() => {}); + const result = { + content: [{ type: "text" as const, text: "Error: command must start with /" }], + details: undefined, + isError: true, + }; + + const component = tool.renderResult!(result, renderOptions, theme, createRenderContext()); + + expect(component).toBeInstanceOf(Text); + const rendered = stripAnsi(component.render(120).join("\n")); + expect(rendered).toContain("Error: command must start with /"); + }); + + it("renders arrow with suggestion when details present but no summary", () => { + const tool = createSuggestNextToolDefinition(() => {}); + const result = { + content: [{ type: "text" as const, text: "Suggestion registered: /compact" }], + details: { suggestion: "/compact" }, + isError: false, + }; + + const component = tool.renderResult!(result, renderOptions, theme, createRenderContext()); + + expect(component).toBeInstanceOf(Text); + const rendered = stripAnsi(component.render(120).join("\n")); + expect(rendered).toContain("→ /compact"); + }); + + it("renders Container with summary markdown and arrow when summary present", () => { + const tool = createSuggestNextToolDefinition(() => {}); + const result = { + content: [{ type: "text" as const, text: "Suggestion registered: /skill:mach6-push" }], + details: { suggestion: "/skill:mach6-push", summary: "Updated the auth handler." }, + isError: false, + }; + + const component = tool.renderResult!(result, renderOptions, theme, createRenderContext()); + + expect(component).toBeInstanceOf(Container); + const rendered = stripAnsi(component.render(120).join("\n")); + expect(rendered).toContain("Updated the auth handler."); + expect(rendered).toContain("→ /skill:mach6-push"); + }); + + it("reuses lastComponent Text on re-render without summary", () => { + const tool = createSuggestNextToolDefinition(() => {}); + const existingText = new Text("old", 0, 0); + const result = { + content: [{ type: "text" as const, text: "Suggestion registered: /compact" }], + details: { suggestion: "/compact" }, + isError: false, + }; + + const component = tool.renderResult!( + result, + renderOptions, + theme, + createRenderContext({ lastComponent: existingText }), + ); + + expect(component).toBe(existingText); + const rendered = stripAnsi(component.render(120).join("\n")); + expect(rendered).toContain("→ /compact"); + }); + + it("reuses lastComponent Container on re-render with summary", () => { + const tool = createSuggestNextToolDefinition(() => {}); + const existingContainer = new Container(); + const result = { + content: [{ type: "text" as const, text: "Suggestion registered: /compact" }], + details: { suggestion: "/compact", summary: "Done." }, + isError: false, + }; + + const component = tool.renderResult!( + result, + renderOptions, + theme, + createRenderContext({ lastComponent: existingContainer }), + ); + + expect(component).toBe(existingContainer); + const rendered = stripAnsi(component.render(120).join("\n")); + expect(rendered).toContain("Done."); + expect(rendered).toContain("→ /compact"); + }); + }); }); From ab97a4e89c5ca54f74d7be77a5a053fbf56febca Mon Sep 17 00:00:00 2001 From: m-aebrer Date: Fri, 15 May 2026 16:07:54 -0400 Subject: [PATCH 6/6] chore: bump version to 2.19.2 --- README.md | 2 +- package-lock.json | 16 ++++++++-------- package.json | 2 +- packages/agent/package.json | 2 +- packages/ai/package.json | 2 +- packages/coding-agent/README.md | 2 +- packages/coding-agent/package.json | 2 +- .../semantic-search/.claude-plugin/plugin.json | 2 +- packages/semantic-search/package.json | 2 +- packages/telegram/package.json | 2 +- packages/tui/package.json | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index b9f2841..e728105 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ See the full coding-agent docs in [packages/coding-agent](packages/coding-agent/ ### Tools and interaction -dreb ships with 11 built-in tools: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`, `web_search`, `web_fetch`, `subagent`, and `wait`. Four more tools are always active when available: `search` for semantic codebase search, `skill` for loading workflows, `tasks_update` for visible task tracking, and `suggest_next` for ghost text command suggestions (Tab to accept). +dreb ships with 11 built-in tools: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`, `web_search`, `web_fetch`, `subagent`, and `wait`. Three more tools are always active: `search` for semantic codebase search, `skill` for loading workflows, and `tasks_update` for visible task tracking. `suggest_next` (ghost text command suggestions, Tab to accept) is active by default but excluded when `--tools` is specified. Interactive mode adds slash commands such as `/model`, `/settings`, `/resume`, `/tree`, `/fork`, `/compact`, `/dream`, `/buddy`, `/export`, `/reload`, `/hotkeys`, and `/changelog`. The message queue lets you steer a running agent or queue follow-up work without waiting for the current turn to finish. diff --git a/package-lock.json b/package-lock.json index 274ba36..696b274 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dreb", - "version": "2.19.1", + "version": "2.19.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dreb", - "version": "2.19.1", + "version": "2.19.2", "workspaces": [ "packages/*", "packages/coding-agent/examples/extensions/with-deps", @@ -8763,7 +8763,7 @@ }, "packages/agent": { "name": "@dreb/agent-core", - "version": "2.19.1", + "version": "2.19.2", "license": "MIT", "dependencies": { "@dreb/ai": "*" @@ -8792,7 +8792,7 @@ }, "packages/ai": { "name": "@dreb/ai", - "version": "2.19.1", + "version": "2.19.2", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.73.0", @@ -8848,7 +8848,7 @@ }, "packages/coding-agent": { "name": "@dreb/coding-agent", - "version": "2.19.1", + "version": "2.19.2", "license": "MIT", "dependencies": { "@dreb/agent-core": "*", @@ -8963,7 +8963,7 @@ }, "packages/semantic-search": { "name": "@dreb/semantic-search", - "version": "2.19.1", + "version": "2.19.2", "license": "MIT", "dependencies": { "@huggingface/transformers": "^4.0.1", @@ -9012,7 +9012,7 @@ }, "packages/telegram": { "name": "@dreb/telegram", - "version": "2.19.1", + "version": "2.19.2", "dependencies": { "@dreb/coding-agent": "*", "grammy": "^1.35.0" @@ -9044,7 +9044,7 @@ }, "packages/tui": { "name": "@dreb/tui", - "version": "2.19.1", + "version": "2.19.2", "license": "MIT", "dependencies": { "@types/mime-types": "^2.1.4", diff --git a/package.json b/package.json index 445ef44..7bbec91 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "engines": { "node": ">=20.0.0" }, - "version": "2.19.1", + "version": "2.19.2", "dependencies": { "@mariozechner/jiti": "^2.6.5", "@dreb/coding-agent": "*", diff --git a/packages/agent/package.json b/packages/agent/package.json index 051292c..66ac450 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -1,6 +1,6 @@ { "name": "@dreb/agent-core", - "version": "2.19.1", + "version": "2.19.2", "description": "General-purpose agent with transport abstraction, state management, and attachment support", "type": "module", "main": "./dist/index.js", diff --git a/packages/ai/package.json b/packages/ai/package.json index 871c955..de003b9 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@dreb/ai", - "version": "2.19.1", + "version": "2.19.2", "description": "Unified LLM API with automatic model discovery and provider configuration", "type": "module", "main": "./dist/index.js", diff --git a/packages/coding-agent/README.md b/packages/coding-agent/README.md index 00ade7e..8a6b648 100644 --- a/packages/coding-agent/README.md +++ b/packages/coding-agent/README.md @@ -62,7 +62,7 @@ dreb Or use a custom provider (corporate proxy, Bedrock, etc.) — see [Custom providers & models](#providers--models). -Then just talk to dreb. All 11 built-in tools are enabled by default: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`, `web_search`, `web_fetch`, `subagent`, and `wait`. Use `--tools` to restrict to a subset (e.g., `--tools read,grep,find,ls` for read-only). Four additional tools — `search`, `skill`, `tasks_update`, and `suggest_next` — are always active. The model uses these to fulfill your requests. Add capabilities via [skills](#skills), [prompt templates](#prompt-templates), [extensions](#extensions), or [packages](#packages). +Then just talk to dreb. All 11 built-in tools are enabled by default: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`, `web_search`, `web_fetch`, `subagent`, and `wait`. Use `--tools` to restrict to a subset (e.g., `--tools read,grep,find,ls` for read-only). Three additional tools — `search`, `skill`, and `tasks_update` — are always active regardless of `--tools`. `suggest_next` is active by default but excluded when `--tools` is specified. The model uses these to fulfill your requests. Add capabilities via [skills](#skills), [prompt templates](#prompt-templates), [extensions](#extensions), or [packages](#packages). **Also available:** [`@dreb/telegram`](https://www.npmjs.com/package/@dreb/telegram) — run dreb as a Telegram bot (`npm install -g @dreb/telegram`). diff --git a/packages/coding-agent/package.json b/packages/coding-agent/package.json index 6fa40d7..6fbce49 100644 --- a/packages/coding-agent/package.json +++ b/packages/coding-agent/package.json @@ -1,6 +1,6 @@ { "name": "@dreb/coding-agent", - "version": "2.19.1", + "version": "2.19.2", "description": "Coding agent CLI with read, bash, edit, write tools and session management", "type": "module", "drebConfig": { diff --git a/packages/semantic-search/.claude-plugin/plugin.json b/packages/semantic-search/.claude-plugin/plugin.json index 2c861cf..d4839e5 100644 --- a/packages/semantic-search/.claude-plugin/plugin.json +++ b/packages/semantic-search/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "semantic-search", "description": "Semantic codebase search — natural language queries over code and docs using embeddings, tree-sitter parsing, and POEM multi-signal ranking", - "version": "2.19.1", + "version": "2.19.2", "author": { "name": "Drew Brereton" }, diff --git a/packages/semantic-search/package.json b/packages/semantic-search/package.json index e8b9740..efd7aa1 100644 --- a/packages/semantic-search/package.json +++ b/packages/semantic-search/package.json @@ -1,6 +1,6 @@ { "name": "@dreb/semantic-search", - "version": "2.19.1", + "version": "2.19.2", "description": "Semantic codebase search engine with embedding-based ranking and MCP server", "publishConfig": { "access": "public" diff --git a/packages/telegram/package.json b/packages/telegram/package.json index 69ac8bc..9cd198b 100644 --- a/packages/telegram/package.json +++ b/packages/telegram/package.json @@ -1,6 +1,6 @@ { "name": "@dreb/telegram", - "version": "2.19.1", + "version": "2.19.2", "description": "Telegram bot frontend for dreb coding agent", "type": "module", "main": "./dist/index.js", diff --git a/packages/tui/package.json b/packages/tui/package.json index 36fdd08..035af85 100644 --- a/packages/tui/package.json +++ b/packages/tui/package.json @@ -1,6 +1,6 @@ { "name": "@dreb/tui", - "version": "2.19.1", + "version": "2.19.2", "description": "Terminal User Interface library with differential rendering for efficient text-based applications", "type": "module", "main": "dist/index.js",