← opencode report

T13 — Tool description .txt pattern

Tách description ra file .txt riêng với template variable injection — description đóng vai trò như 'mini system prompt' cho từng tool.
Nhóm: C — Tool DesignFile: tool/*.txt (mỗi tool 1 file)ID: C.1 / T13Status: Stable

Tổng quan Tool Design

Tại sao quan trọng. Tool description thường dài 100–300 dòng và đóng vai trò như một "mini system prompt" chuyên biệt cho tool đó — quyết định model biết tool làm gì, nên dùng khi nào, và không nên dùng khi nào. Nhét text dài vào source TypeScript gây ra vấn đề: khó đọc diff khi review, escape ký tự phức tạp, và engineering team bị bottleneck khi muốn thử nghiệm prompt.

opencode giải quyết bằng cách tách description ra file .txt bên cạnh file .ts. Bun (runtime của opencode) hỗ trợ import text file trực tiếp. Description hỗ trợ template variable như {PLATFORM}, {SHELL}, {MAX_LINES} — được inject lúc tool khởi tạo.

Điểm đặc biệt so với harness khác: Đây là pattern hiếm — hầu hết harness nhúng description thẳng vào code (Python docstring, TypeScript template literal). opencode coi description như artifact riêng có lifecycle độc lập, giúp prompt engineer và software engineer làm việc song song không conflict.

Phân tích code chi tiết Anatomy

Cặp .ts + .txt cho mỗi tool

Mỗi tool trong opencode có cặp file:

tool/ ├── bash.ts ← implementation, schema, handler ├── bash.txt ← description (template với {PLATFORM}, {SHELL}) ├── edit.ts ├── edit.txt ├── read.ts ├── read.txt ...

tool/bash.ts — import và inject template

TS
{`
import DESCRIPTION from "./bash.txt"
import { Tool } from "./tool"

export const BashTool = Tool.define("bash", Effect.gen(function* () => {
  const agent = yield* Agent
  return {
    description: renderTemplate(DESCRIPTION, {
      PLATFORM: process.platform,
      SHELL:    process.env.SHELL ?? "bash",
      MAX_LINES: 2000,
    }),
    parameters: z.object({
      command: z.string(),
      timeout: z.number().optional(),
    }),
    // execute: ...
  }
}))
`}

tool/bash.txt — excerpt

TEXT
{`
Execute shell commands in a persistent {SHELL} session on {PLATFORM}.

<important-rules>
- DO NOT use `find` or `grep` — use the dedicated Grep/Glob tools instead.
- Quote paths with spaces: cd "path with spaces"
- Output is truncated to {MAX_LINES} lines. Full output saved to disk.
- Prefer absolute paths to avoid cwd ambiguity.
</important-rules>

<when-to-use>
Use when you need to run build commands, install packages, execute tests,
or any shell operation not covered by specialized tools.
</when-to-use>

<when-not-to-use>
- Reading files: use Read tool instead (structured output)
- Searching code: use Grep/Glob tools instead (faster, scoped)
- Editing files: use Edit tool instead (fuzzy matching + validation)
</when-not-to-use>
`}

Template variable injection

Hàm renderTemplate đơn giản — thay thế {KEY} trong text bằng giá trị tương ứng. Điểm mạnh: description có thể tự điều chỉnh theo môi trường (Windows vs macOS vs Linux, bash vs zsh vs fish).

renderTemplate — generic implementation

TS
{`
function renderTemplate(
  template: string,
  vars: Record<string, string | number>
): string {
  return template.replace(/\{(\w+)\}/g, (_, key) => {
    const val = vars[key]
    if (val === undefined) throw new Error(`Template var {${key}} not provided`)
    return String(val)
  })
}

// A/B test: swap file nội dung mà không đổi code
const descriptionV2 = renderTemplate(await readFile("bash-v2.txt", "utf8"), vars)
`}

Tương tác với kỹ thuật khác Interaction

T13 kết hợp chặt với các kỹ thuật sau:

  • T14 (Effect lazy tool init): Template rendering xảy ra bên trong Effect.gen block — chỉ chạy 1 lần lúc define tool, không phải mỗi lần execute.
  • T16 (Zod validation): Description và schema đi cùng nhau — description dạy model format args, Zod validate args nhận được.
  • T27 (Model-specific system prompt): System prompt cũng dùng pattern .txt file (anthropic.txt, gpt.txt...) — T13 là ứng dụng của pattern đó cho tool-level.
Cache behavior: Description là static sau khi tool init — Anthropic prompt cache có thể cache description trong system block. Thay đổi description (swap .txt) chỉ invalidate cache 1 lần khi tool được define lại.

Failure modes Failure

1. Template variable typo

Nếu .txt file dùng {PLATFROM} (typo) thay vì {PLATFORM}, hàm renderTemplate sẽ: — Nếu strict: throw lúc tool init (tốt, fail fast). — Nếu không strict: để nguyên literal {PLATFROM} trong description → model thấy text rác.

Fix: Luôn validate template vars sau render — assert không còn {...} nào trong output.

2. Bundler không bundle .txt

Bun hỗ trợ text import native. Node.js không — cần loader hoặc đọc file thủ công lúc runtime. Nếu quên config, import sẽ trả undefined hoặc throw.

3. Description drift

Engineer sửa behavior của tool (code) nhưng quên update .txt → description mô tả sai hành vi thực → model dùng tool sai cách. Cần test đảm bảo description sync với behavior.

So sánh với các harness khác Compare

HarnessCách quản lý tool descriptionNhận xét
opencodeFile .txt riêng, template vars, import staticSạch nhất, dễ A/B test
Claude CodeTypeScript template literal trong file sourceDài nhưng type-safe, khó version control riêng
AiderPython docstring trong classGắn chặt với code, không template
ClineHardcoded string trong register() callĐơn giản, không tách được
OpenHarnessPython string constant + f-stringTương tự Aider nhưng hỗ trợ interpolation

Implementation recipe Recipe

Áp dụng pattern này vào dự án của bạn

Nếu dùng Bun:

TS
{`
// tsconfig.json — thêm để TypeScript hiểu text import
{
  "compilerOptions": {
    "moduleResolution": "bundler"
  }
}

// my-tool.ts
import DESCRIPTION from "./my-tool.txt"

export const MyTool = defineTool({
  name: "my_tool",
  description: renderTemplate(DESCRIPTION, {
    MAX_OUTPUT: 5000,
    PLATFORM: process.platform,
  }),
  // ...
})
`}

Nếu dùng Node.js (không có Bun text import):

TS
{`
import { readFileSync } from "node:fs"
import { join, dirname } from "node:path"
import { fileURLToPath } from "node:url"

const __dirname = dirname(fileURLToPath(import.meta.url))
const DESCRIPTION = readFileSync(join(__dirname, "my-tool.txt"), "utf8")
`}
Recommendation: Dùng pattern này cho bất kỳ tool nào có description > 5 dòng. Đặt .txt cùng thư mục với .ts. Template vars cho platform, shell, và bất kỳ giá trị runtime nào cần inject.

Tham khảo Refs