Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "*",
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/ai/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/coding-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

Expand Down
2 changes: 1 addition & 1 deletion packages/coding-agent/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
8 changes: 6 additions & 2 deletions packages/coding-agent/src/core/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 6 additions & 5 deletions packages/coding-agent/src/core/tools/subagent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"];

Expand Down Expand Up @@ -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) {
Expand Down
56 changes: 40 additions & 16 deletions packages/coding-agent/src/core/tools/suggest-next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
* 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";

// ============================================================================
// Types

export interface SuggestNextDetails {
suggestion: string;
summary?: string;
}

export type SuggestNextCallback = (suggestion: string) => void;
Expand All @@ -25,6 +27,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<typeof suggestNextSchema>;
Expand All @@ -37,18 +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) : "";
}
return theme.fg("toolOutput", `→ ${details.suggestion}`);
}

// ============================================================================
// Tool definition factory

Expand All @@ -70,9 +66,11 @@ 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",
"Calling this tool ends your turn automatically — do not call wait afterwards",
],

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("/")) {
Expand All @@ -84,9 +82,18 @@ export function createSuggestNextToolDefinition(

onSuggest(command);

// 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}` }],
details: { suggestion: command },
details: { suggestion: command, summary: sanitizedSummary },
endTurn: true,
};
},

Expand All @@ -97,8 +104,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;
},
};
Expand Down
Loading
Loading