fix(opencode): strip backticks from plugin reminder to prevent silent command substitution (#1413)

The opencode plugin template embedded the reminder string with backticks
around `graphify query "<question>"`. Because the plugin prepends
`echo "<reminder>" && <cmd>` to the user's bash command, those backticks
triggered bash command substitution: every grep/rg/find invocation silently
ran `graphify query "<question>"` and substituted its output
("No matching nodes found.") into the reminder text shown to the agent.

This both corrupted tool output with graphify noise, and actually spawned
a graphify process + loaded graph.json + ran a BFS traversal with the
literal token <question> on every search.

Fix: remove the backticks. Adds a guard comment in the template so future
editors don't reintroduce the bug, and a regression test that asserts the
reminder string contains no backticks and no $() constructs.
This commit is contained in:
WSHAPER
2026-06-22 22:54:03 +01:00
committed by safishamsi
parent aa06e10fa5
commit 2323ce1937
2 changed files with 29 additions and 1 deletions
+7 -1
View File
@@ -1380,6 +1380,12 @@ def _uninstall_kilo_plugin(project_dir: Path) -> None:
_OPENCODE_PLUGIN_JS = """\
// graphify OpenCode plugin
// Injects a knowledge graph reminder before bash tool calls when the graph exists.
//
// IMPORTANT: keep the reminder string free of backticks and $(...) constructs.
// The hook prepends `echo "<reminder>" && <cmd>` to the user's bash command;
// backticks inside the double-quoted echo trigger bash command substitution,
// which both corrupts tool output and silently executes the very graphify
// command we are only suggesting. Plain words render fine in opencode's TUI.
import { existsSync } from "fs";
import { join } from "path";
@@ -1393,7 +1399,7 @@ export const GraphifyPlugin = async ({ directory }) => {
if (input.tool === "bash") {
output.args.command =
'echo "[graphify] knowledge graph at graphify-out/. For focused questions, run \\`graphify query \\"<question>\\"\\` (scoped subgraph, usually much smaller than GRAPH_REPORT.md) instead of grepping raw files. Read GRAPH_REPORT.md only for broad architecture context." && ' +
'echo "[graphify] knowledge graph at graphify-out/. For focused questions, run graphify query with your question (scoped subgraph, usually much smaller than GRAPH_REPORT.md) instead of grepping raw files. Read GRAPH_REPORT.md only for broad architecture context." && ' +
output.args.command;
reminded = true;
}
+22
View File
@@ -599,6 +599,28 @@ def test_opencode_agents_install_writes_plugin(tmp_path):
assert "tool.execute.before" in plugin.read_text()
def test_opencode_plugin_reminder_has_no_backticks(tmp_path):
"""The bash reminder string must not contain backticks or $(...) (regression test for #1413).
The plugin prepends `echo "<reminder>" && <cmd>` to the user's bash command.
Backticks or $() inside the reminder trigger bash command substitution
when the echo runs, which both corrupts tool output and silently executes
the very graphify command we are only suggesting.
"""
_agents_install(tmp_path, "opencode")
plugin = tmp_path / ".opencode" / "plugins" / "graphify.js"
body = plugin.read_text()
# Extract the echoed reminder string literal between the double-quotes
# of the `output.args.command = 'echo "..." && ' +` line.
import re
m = re.search(r'echo "([^"]*)"', body)
assert m, "echo reminder not found in plugin body"
reminder = m.group(1)
assert "`" not in reminder, f"backtick in reminder would trigger command substitution: {reminder!r}"
assert "$(" not in reminder, f"$() in reminder would trigger command substitution: {reminder!r}"
def test_opencode_agents_install_registers_plugin_in_config(tmp_path):
"""opencode install registers the plugin in .opencode/opencode.json."""
_agents_install(tmp_path, "opencode")