#20646: feat: add file-writer extension for shell-safe text passing
size: S
Cluster:
Plugin Hook Enhancements
## Summary
- Adds a `write_file` tool extension that writes text content to temporary files using structured JSON parameters, completely bypassing shell escaping
- Solves the fundamental problem that the `exec` tool passes commands as shell strings, causing long text with quotes, parentheses, URLs, and non-ASCII characters to break
- Security: writes restricted to `/tmp/`, `~/.cache/`, and optionally configured directories
## Motivation
When the LLM needs to pass long text to a CLI tool via `exec`, shell quoting is fragile:
```
exec("web-save memo --body 'This has "quotes" and (parens) and https://example.com/page?id=1'")
```
This inevitably breaks. The `write_file` tool lets the LLM write the content to a temp file first:
```
write_file({ file_path: "/tmp/memo.txt", content: "..." })
exec("web-save memo --file /tmp/memo.txt")
```
## Files
| File | Description |
|------|-------------|
| `extensions/file-writer/index.ts` | Plugin entry point |
| `extensions/file-writer/openclaw.plugin.json` | Plugin manifest with `allowedDirs` config |
| `extensions/file-writer/package.json` | Package metadata |
| `extensions/file-writer/src/file-writer-tool.ts` | Tool implementation |
## Configuration
Optional `allowedDirs` config to extend the default allowed directories:
```json
{
"plugins": {
"file-writer": {
"allowedDirs": ["/home/user/data"]
}
}
}
```
## Test plan
- [x] `tsgo --noEmit` passes
- [ ] Manual test: write a file to `/tmp/test.txt` via the tool, verify content
- [ ] Manual test: write to a disallowed path, verify rejection
Related #19496
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds a `write_file` tool extension that enables LLMs to write text content to temporary files using structured JSON parameters, solving shell escaping issues when passing long text with special characters to CLI tools via `exec`.
- New plugin at `extensions/file-writer/` with standard structure
- Security: restricts writes to `/tmp/`, `/private/tmp/`, `~/.cache/`, and optionally configured directories
- Path validation logic resolves paths and checks they're under allowed directories
- Follows existing tool patterns from `llm-task` extension
<h3>Confidence Score: 3/5</h3>
- Safe to merge after fixing the path normalization issue for user-configured directories
- The implementation follows existing patterns and has good path validation, but user-configured `allowedDirs` must be normalized to prevent security bypass. The single logic issue is straightforward to fix.
- Review `extensions/file-writer/src/file-writer-tool.ts:30` for the path normalization fix
<sub>Last reviewed commit: b6695f9</sub>
<!-- greptile_other_comments_section -->
<sub>(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#20647: feat: add exec-passthrough extension for direct output delivery
by okuyam2y · 2026-02-19
69.8%
#8846: fix(tools): block LLM writes to hooks directories
by yubrew · 2026-02-04
69.8%
#17667: feat: tool-hooks extension — run shell commands on tool calls
by FaradayHunt · 2026-02-16
68.9%
#10189: fix: resolve file_path param in tool display for read/write tools
by Yida-Dev · 2026-02-06
67.3%
#18022: feat(tools): Structured Config Editor for JSON/YAML/TOML
by stakeswky · 2026-02-16
66.7%
#18992: fix: suppress spurious tool error warnings for read-only exec commands
by Phineas1500 · 2026-02-17
65.9%
#17463: fix: write config files with explicit 0o600 mode instead of post-wr...
by miclaldogan · 2026-02-15
65.8%
#14419: feat(extensions): add telegram-files Mini App
by audichuang · 2026-02-12
65.8%
#23557: fix: catch session JSONL write failures instead of crashing
by Joe3112 · 2026-02-22
65.6%
#23803: Fix tool metadata truncation
by kamal-ayman · 2026-02-22
65.5%