@dreb/coding-agent 2.27.2 → 2.28.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"suggest-next.d.ts","sourceRoot":"","sources":["../../../src/core/tools/suggest-next.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAK7D,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;AAK/D,QAAA,MAAM,iBAAiB;;;EAUrB,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAahE,wBAAgB,+BAA+B,CAC9C,SAAS,EAAE,mBAAmB,GAC5B,cAAc,CAAC,OAAO,iBAAiB,EAAE,kBAAkB,GAAG,SAAS,CAAC,CA4E1E","sourcesContent":["/**\n * Suggest next command tool.\n *\n * Allows the agent to suggest a command the user might want to run next.\n * The suggestion is shown as ghost text in the editor prompt (Tab to accept).\n */\n\nimport { Container, Markdown, Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { getMarkdownTheme } from \"../../modes/interactive/theme/theme.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\n\n// ============================================================================\n// Types\n\nexport interface SuggestNextDetails {\n\tsuggestion: string;\n\tsummary?: string;\n}\n\nexport type SuggestNextCallback = (suggestion: string) => void;\n\n// ============================================================================\n// Schema\n\nconst suggestNextSchema = Type.Object({\n\tcommand: Type.String({\n\t\tdescription: \"The suggested command for the user to run next (e.g. /skill:mach6-push, /compact)\",\n\t}),\n\tsummary: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Brief markdown summary of the work done this turn. Displayed to the user as the final message before the suggestion.\",\n\t\t}),\n\t),\n});\n\nexport type SuggestNextInput = Static<typeof suggestNextSchema>;\n\n// ============================================================================\n// Render helpers\n\nfunction formatSuggestNextCall(args: { command?: string } | undefined, theme: any): string {\n\tconst cmd = args?.command ?? \"\";\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"suggest_next\"))} ${theme.fg(\"accent\", cmd)}`;\n}\n\n// ============================================================================\n// Tool definition factory\n\nexport function createSuggestNextToolDefinition(\n\tonSuggest: SuggestNextCallback,\n): ToolDefinition<typeof suggestNextSchema, SuggestNextDetails | undefined> {\n\treturn {\n\t\tname: \"suggest_next\",\n\t\tlabel: \"suggest_next\",\n\t\tdescription:\n\t\t\t\"Suggest a command for the user to run next. Shows as ghost text in the prompt that the user can Tab-accept.\",\n\n\t\tparameters: suggestNextSchema,\n\n\t\tpromptSnippet: \"Suggest a next command (shown as ghost text the user can Tab-accept)\",\n\n\t\tpromptGuidelines: [\n\t\t\t\"Call suggest_next at the end of your turn when there's a clear next action the user might want\",\n\t\t\t\"Use full command syntax: /skill:name args, /compact, etc.\",\n\t\t\t\"Only suggest one command — pick the most likely next step\",\n\t\t\t\"Don't suggest if the conversation is open-ended with no obvious next action\",\n\t\t\t\"Include a brief summary of work done in the `summary` parameter — this is your last chance to communicate before the turn ends\",\n\t\t\t\"Calling this tool ends your turn automatically — do not call wait afterwards\",\n\t\t],\n\n\t\tasync execute(_toolCallId, { command: rawCommand, summary }: SuggestNextInput, _signal?, _onUpdate?, _ctx?) {\n\t\t\t// Strip control characters (newlines, tabs, etc.) that would corrupt TUI rendering\n\t\t\tconst command = rawCommand?.replace(/[\\x00-\\x1f\\x7f]/g, \"\").trim();\n\t\t\tif (!command || !command.startsWith(\"/\")) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: \"Error: command must start with /\" }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tonSuggest(command);\n\n\t\t\t// Convert literal \\n sequences to actual newlines (LLMs emit these in XML tool calls),\n\t\t\t// then strip control characters (preserve only newlines for markdown)\n\t\t\tconst sanitizedSummary =\n\t\t\t\tsummary\n\t\t\t\t\t?.replace(/\\\\n/g, \"\\n\")\n\t\t\t\t\t.replace(/[\\x00-\\x09\\x0b-\\x1f\\x7f]/g, \"\")\n\t\t\t\t\t.trim() || undefined;\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Suggestion registered: ${command}` }],\n\t\t\t\tdetails: { suggestion: command, summary: sanitizedSummary },\n\t\t\t\tendTurn: true,\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatSuggestNextCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst details = (result as any).details as SuggestNextDetails | undefined;\n\t\t\tif (!details) {\n\t\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\t\tconst content = result.content?.[0];\n\t\t\t\tconst msg = content?.type === \"text\" && content.text ? content.text : \"\";\n\t\t\t\ttext.setText(theme.fg(\"toolOutput\", msg));\n\t\t\t\treturn text;\n\t\t\t}\n\n\t\t\tif (details.summary) {\n\t\t\t\tconst container = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcontainer.clear();\n\t\t\t\tcontainer.addChild(new Markdown(details.summary, 0, 0, getMarkdownTheme()));\n\t\t\t\tcontainer.addChild(new Text(theme.fg(\"toolOutput\", `→ ${details.suggestion}`), 0, 0));\n\t\t\t\treturn container;\n\t\t\t}\n\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(theme.fg(\"toolOutput\", `→ ${details.suggestion}`));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"suggest-next.d.ts","sourceRoot":"","sources":["../../../src/core/tools/suggest-next.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAK7D,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;AAK/D,QAAA,MAAM,iBAAiB;;;EAUrB,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAahE,wBAAgB,+BAA+B,CAC9C,SAAS,EAAE,mBAAmB,GAC5B,cAAc,CAAC,OAAO,iBAAiB,EAAE,kBAAkB,GAAG,SAAS,CAAC,CA6E1E","sourcesContent":["/**\n * Suggest next command tool.\n *\n * Allows the agent to suggest a command the user might want to run next.\n * The suggestion is shown as ghost text in the editor prompt (Tab to accept).\n */\n\nimport { Container, Markdown, Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { getMarkdownTheme } from \"../../modes/interactive/theme/theme.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\n\n// ============================================================================\n// Types\n\nexport interface SuggestNextDetails {\n\tsuggestion: string;\n\tsummary?: string;\n}\n\nexport type SuggestNextCallback = (suggestion: string) => void;\n\n// ============================================================================\n// Schema\n\nconst suggestNextSchema = Type.Object({\n\tcommand: Type.String({\n\t\tdescription: \"The suggested command for the user to run next (e.g. /skill:mach6-push, /compact, npm run build)\",\n\t}),\n\tsummary: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Brief markdown summary of the work done this turn. Displayed to the user as the final message before the suggestion.\",\n\t\t}),\n\t),\n});\n\nexport type SuggestNextInput = Static<typeof suggestNextSchema>;\n\n// ============================================================================\n// Render helpers\n\nfunction formatSuggestNextCall(args: { command?: string } | undefined, theme: any): string {\n\tconst cmd = args?.command ?? \"\";\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"suggest_next\"))} ${theme.fg(\"accent\", cmd)}`;\n}\n\n// ============================================================================\n// Tool definition factory\n\nexport function createSuggestNextToolDefinition(\n\tonSuggest: SuggestNextCallback,\n): ToolDefinition<typeof suggestNextSchema, SuggestNextDetails | undefined> {\n\treturn {\n\t\tname: \"suggest_next\",\n\t\tlabel: \"suggest_next\",\n\t\tdescription:\n\t\t\t\"Suggest a command for the user to run next. Shows as ghost text in the prompt that the user can Tab-accept.\",\n\n\t\tparameters: suggestNextSchema,\n\n\t\tpromptSnippet: \"Suggest a next command (shown as ghost text the user can Tab-accept)\",\n\n\t\tpromptGuidelines: [\n\t\t\t\"Call suggest_next at the end of your turn when there's a clear next action the user might want\",\n\t\t\t\"Suggest a command the user can run: /skill:name args, /compact, npm run build, etc.\",\n\t\t\t\"Only suggest one command — pick the most likely next step\",\n\t\t\t\"Don't suggest if the conversation is open-ended with no obvious next action\",\n\t\t\t\"Include a brief summary of work done in the `summary` parameter — this is your last chance to communicate before the turn ends\",\n\t\t\t\"Calling this tool ends your turn automatically — do not call wait afterwards\",\n\t\t],\n\n\t\tasync execute(_toolCallId, { command: rawCommand, summary }: SuggestNextInput, _signal?, _onUpdate?, _ctx?) {\n\t\t\t// Strip control characters (newlines, tabs, etc.) that would corrupt TUI rendering\n\t\t\tconst command = rawCommand?.replace(/[\\x00-\\x1f\\x7f]/g, \"\").trim();\n\t\t\tif (!command) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: \"Error: command is empty\" }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t\tendTurn: true,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tonSuggest(command);\n\n\t\t\t// Convert literal \\n sequences to actual newlines (LLMs emit these in XML tool calls),\n\t\t\t// then strip control characters (preserve only newlines for markdown)\n\t\t\tconst sanitizedSummary =\n\t\t\t\tsummary\n\t\t\t\t\t?.replace(/\\\\n/g, \"\\n\")\n\t\t\t\t\t.replace(/[\\x00-\\x09\\x0b-\\x1f\\x7f]/g, \"\")\n\t\t\t\t\t.trim() || undefined;\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Suggestion registered: ${command}` }],\n\t\t\t\tdetails: { suggestion: command, summary: sanitizedSummary },\n\t\t\t\tendTurn: true,\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatSuggestNextCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst details = (result as any).details as SuggestNextDetails | undefined;\n\t\t\tif (!details) {\n\t\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\t\tconst content = result.content?.[0];\n\t\t\t\tconst msg = content?.type === \"text\" && content.text ? content.text : \"\";\n\t\t\t\ttext.setText(theme.fg(\"toolOutput\", msg));\n\t\t\t\treturn text;\n\t\t\t}\n\n\t\t\tif (details.summary) {\n\t\t\t\tconst container = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcontainer.clear();\n\t\t\t\tcontainer.addChild(new Markdown(details.summary, 0, 0, getMarkdownTheme()));\n\t\t\t\tcontainer.addChild(new Text(theme.fg(\"toolOutput\", `→ ${details.suggestion}`), 0, 0));\n\t\t\t\treturn container;\n\t\t\t}\n\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(theme.fg(\"toolOutput\", `→ ${details.suggestion}`));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n"]}
@@ -11,7 +11,7 @@ import { getMarkdownTheme } from "../../modes/interactive/theme/theme.js";
11
11
  // Schema
12
12
  const suggestNextSchema = Type.Object({
13
13
  command: Type.String({
14
- description: "The suggested command for the user to run next (e.g. /skill:mach6-push, /compact)",
14
+ description: "The suggested command for the user to run next (e.g. /skill:mach6-push, /compact, npm run build)",
15
15
  }),
16
16
  summary: Type.Optional(Type.String({
17
17
  description: "Brief markdown summary of the work done this turn. Displayed to the user as the final message before the suggestion.",
@@ -34,7 +34,7 @@ export function createSuggestNextToolDefinition(onSuggest) {
34
34
  promptSnippet: "Suggest a next command (shown as ghost text the user can Tab-accept)",
35
35
  promptGuidelines: [
36
36
  "Call suggest_next at the end of your turn when there's a clear next action the user might want",
37
- "Use full command syntax: /skill:name args, /compact, etc.",
37
+ "Suggest a command the user can run: /skill:name args, /compact, npm run build, etc.",
38
38
  "Only suggest one command — pick the most likely next step",
39
39
  "Don't suggest if the conversation is open-ended with no obvious next action",
40
40
  "Include a brief summary of work done in the `summary` parameter — this is your last chance to communicate before the turn ends",
@@ -43,10 +43,11 @@ export function createSuggestNextToolDefinition(onSuggest) {
43
43
  async execute(_toolCallId, { command: rawCommand, summary }, _signal, _onUpdate, _ctx) {
44
44
  // Strip control characters (newlines, tabs, etc.) that would corrupt TUI rendering
45
45
  const command = rawCommand?.replace(/[\x00-\x1f\x7f]/g, "").trim();
46
- if (!command || !command.startsWith("/")) {
46
+ if (!command) {
47
47
  return {
48
- content: [{ type: "text", text: "Error: command must start with /" }],
48
+ content: [{ type: "text", text: "Error: command is empty" }],
49
49
  details: undefined,
50
+ endTurn: true,
50
51
  };
51
52
  }
52
53
  onSuggest(command);
@@ -1 +1 @@
1
- {"version":3,"file":"suggest-next.js","sourceRoot":"","sources":["../../../src/core/tools/suggest-next.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Suggest next command tool.\n *\n * Allows the agent to suggest a command the user might want to run next.\n * The suggestion is shown as ghost text in the editor prompt (Tab to accept).\n */\n\nimport { Container, Markdown, Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { getMarkdownTheme } from \"../../modes/interactive/theme/theme.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\n\n// ============================================================================\n// Types\n\nexport interface SuggestNextDetails {\n\tsuggestion: string;\n\tsummary?: string;\n}\n\nexport type SuggestNextCallback = (suggestion: string) => void;\n\n// ============================================================================\n// Schema\n\nconst suggestNextSchema = Type.Object({\n\tcommand: Type.String({\n\t\tdescription: \"The suggested command for the user to run next (e.g. /skill:mach6-push, /compact)\",\n\t}),\n\tsummary: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Brief markdown summary of the work done this turn. Displayed to the user as the final message before the suggestion.\",\n\t\t}),\n\t),\n});\n\nexport type SuggestNextInput = Static<typeof suggestNextSchema>;\n\n// ============================================================================\n// Render helpers\n\nfunction formatSuggestNextCall(args: { command?: string } | undefined, theme: any): string {\n\tconst cmd = args?.command ?? \"\";\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"suggest_next\"))} ${theme.fg(\"accent\", cmd)}`;\n}\n\n// ============================================================================\n// Tool definition factory\n\nexport function createSuggestNextToolDefinition(\n\tonSuggest: SuggestNextCallback,\n): ToolDefinition<typeof suggestNextSchema, SuggestNextDetails | undefined> {\n\treturn {\n\t\tname: \"suggest_next\",\n\t\tlabel: \"suggest_next\",\n\t\tdescription:\n\t\t\t\"Suggest a command for the user to run next. Shows as ghost text in the prompt that the user can Tab-accept.\",\n\n\t\tparameters: suggestNextSchema,\n\n\t\tpromptSnippet: \"Suggest a next command (shown as ghost text the user can Tab-accept)\",\n\n\t\tpromptGuidelines: [\n\t\t\t\"Call suggest_next at the end of your turn when there's a clear next action the user might want\",\n\t\t\t\"Use full command syntax: /skill:name args, /compact, etc.\",\n\t\t\t\"Only suggest one command — pick the most likely next step\",\n\t\t\t\"Don't suggest if the conversation is open-ended with no obvious next action\",\n\t\t\t\"Include a brief summary of work done in the `summary` parameter — this is your last chance to communicate before the turn ends\",\n\t\t\t\"Calling this tool ends your turn automatically — do not call wait afterwards\",\n\t\t],\n\n\t\tasync execute(_toolCallId, { command: rawCommand, summary }: SuggestNextInput, _signal?, _onUpdate?, _ctx?) {\n\t\t\t// Strip control characters (newlines, tabs, etc.) that would corrupt TUI rendering\n\t\t\tconst command = rawCommand?.replace(/[\\x00-\\x1f\\x7f]/g, \"\").trim();\n\t\t\tif (!command || !command.startsWith(\"/\")) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: \"Error: command must start with /\" }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tonSuggest(command);\n\n\t\t\t// Convert literal \\n sequences to actual newlines (LLMs emit these in XML tool calls),\n\t\t\t// then strip control characters (preserve only newlines for markdown)\n\t\t\tconst sanitizedSummary =\n\t\t\t\tsummary\n\t\t\t\t\t?.replace(/\\\\n/g, \"\\n\")\n\t\t\t\t\t.replace(/[\\x00-\\x09\\x0b-\\x1f\\x7f]/g, \"\")\n\t\t\t\t\t.trim() || undefined;\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Suggestion registered: ${command}` }],\n\t\t\t\tdetails: { suggestion: command, summary: sanitizedSummary },\n\t\t\t\tendTurn: true,\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatSuggestNextCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst details = (result as any).details as SuggestNextDetails | undefined;\n\t\t\tif (!details) {\n\t\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\t\tconst content = result.content?.[0];\n\t\t\t\tconst msg = content?.type === \"text\" && content.text ? content.text : \"\";\n\t\t\t\ttext.setText(theme.fg(\"toolOutput\", msg));\n\t\t\t\treturn text;\n\t\t\t}\n\n\t\t\tif (details.summary) {\n\t\t\t\tconst container = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcontainer.clear();\n\t\t\t\tcontainer.addChild(new Markdown(details.summary, 0, 0, getMarkdownTheme()));\n\t\t\t\tcontainer.addChild(new Text(theme.fg(\"toolOutput\", `→ ${details.suggestion}`), 0, 0));\n\t\t\t\treturn container;\n\t\t\t}\n\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(theme.fg(\"toolOutput\", `→ ${details.suggestion}`));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"suggest-next.js","sourceRoot":"","sources":["../../../src/core/tools/suggest-next.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Suggest next command tool.\n *\n * Allows the agent to suggest a command the user might want to run next.\n * The suggestion is shown as ghost text in the editor prompt (Tab to accept).\n */\n\nimport { Container, Markdown, Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { getMarkdownTheme } from \"../../modes/interactive/theme/theme.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\n\n// ============================================================================\n// Types\n\nexport interface SuggestNextDetails {\n\tsuggestion: string;\n\tsummary?: string;\n}\n\nexport type SuggestNextCallback = (suggestion: string) => void;\n\n// ============================================================================\n// Schema\n\nconst suggestNextSchema = Type.Object({\n\tcommand: Type.String({\n\t\tdescription: \"The suggested command for the user to run next (e.g. /skill:mach6-push, /compact, npm run build)\",\n\t}),\n\tsummary: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Brief markdown summary of the work done this turn. Displayed to the user as the final message before the suggestion.\",\n\t\t}),\n\t),\n});\n\nexport type SuggestNextInput = Static<typeof suggestNextSchema>;\n\n// ============================================================================\n// Render helpers\n\nfunction formatSuggestNextCall(args: { command?: string } | undefined, theme: any): string {\n\tconst cmd = args?.command ?? \"\";\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"suggest_next\"))} ${theme.fg(\"accent\", cmd)}`;\n}\n\n// ============================================================================\n// Tool definition factory\n\nexport function createSuggestNextToolDefinition(\n\tonSuggest: SuggestNextCallback,\n): ToolDefinition<typeof suggestNextSchema, SuggestNextDetails | undefined> {\n\treturn {\n\t\tname: \"suggest_next\",\n\t\tlabel: \"suggest_next\",\n\t\tdescription:\n\t\t\t\"Suggest a command for the user to run next. Shows as ghost text in the prompt that the user can Tab-accept.\",\n\n\t\tparameters: suggestNextSchema,\n\n\t\tpromptSnippet: \"Suggest a next command (shown as ghost text the user can Tab-accept)\",\n\n\t\tpromptGuidelines: [\n\t\t\t\"Call suggest_next at the end of your turn when there's a clear next action the user might want\",\n\t\t\t\"Suggest a command the user can run: /skill:name args, /compact, npm run build, etc.\",\n\t\t\t\"Only suggest one command — pick the most likely next step\",\n\t\t\t\"Don't suggest if the conversation is open-ended with no obvious next action\",\n\t\t\t\"Include a brief summary of work done in the `summary` parameter — this is your last chance to communicate before the turn ends\",\n\t\t\t\"Calling this tool ends your turn automatically — do not call wait afterwards\",\n\t\t],\n\n\t\tasync execute(_toolCallId, { command: rawCommand, summary }: SuggestNextInput, _signal?, _onUpdate?, _ctx?) {\n\t\t\t// Strip control characters (newlines, tabs, etc.) that would corrupt TUI rendering\n\t\t\tconst command = rawCommand?.replace(/[\\x00-\\x1f\\x7f]/g, \"\").trim();\n\t\t\tif (!command) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: \"Error: command is empty\" }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t\tendTurn: true,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tonSuggest(command);\n\n\t\t\t// Convert literal \\n sequences to actual newlines (LLMs emit these in XML tool calls),\n\t\t\t// then strip control characters (preserve only newlines for markdown)\n\t\t\tconst sanitizedSummary =\n\t\t\t\tsummary\n\t\t\t\t\t?.replace(/\\\\n/g, \"\\n\")\n\t\t\t\t\t.replace(/[\\x00-\\x09\\x0b-\\x1f\\x7f]/g, \"\")\n\t\t\t\t\t.trim() || undefined;\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Suggestion registered: ${command}` }],\n\t\t\t\tdetails: { suggestion: command, summary: sanitizedSummary },\n\t\t\t\tendTurn: true,\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatSuggestNextCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst details = (result as any).details as SuggestNextDetails | undefined;\n\t\t\tif (!details) {\n\t\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\t\tconst content = result.content?.[0];\n\t\t\t\tconst msg = content?.type === \"text\" && content.text ? content.text : \"\";\n\t\t\t\ttext.setText(theme.fg(\"toolOutput\", msg));\n\t\t\t\treturn text;\n\t\t\t}\n\n\t\t\tif (details.summary) {\n\t\t\t\tconst container = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcontainer.clear();\n\t\t\t\tcontainer.addChild(new Markdown(details.summary, 0, 0, getMarkdownTheme()));\n\t\t\t\tcontainer.addChild(new Text(theme.fg(\"toolOutput\", `→ ${details.suggestion}`), 0, 0));\n\t\t\t\treturn container;\n\t\t\t}\n\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(theme.fg(\"toolOutput\", `→ ${details.suggestion}`));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreb/coding-agent",
3
- "version": "2.27.2",
3
+ "version": "2.28.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "drebConfig": {