1. ๆถๆๆฆ่ง
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ opencode Todo ็ณป็ปๆถๆ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ AI Agent โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ todoread โ โ todowrite โ โ ไธคไธชๆ็ฎๅทฅๅ
ท โ
โ โ (ๆ ๅๆฐ) โ โ (todos[]) โ โ
โ โโโโโโโโฌโโโโโโโ โโโโโโโโฌโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโโฌโโโโโโโโโโ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโ โ
โ โ Todo Module โ โ
โ โ (todo.ts) โ โ
โ โโโโโโโโโฌโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโผโโโโโโโโโโ โ
โ โผ โผ โผ โ
โ โโโโโโโ โโโโโโโ โโโโโโโโโโ โ
โ โ get โ โupdateโ โ Event โ โ Bus ไบไปถๅๅธ โ
โ โโโโโโโ โโโโโโโ โโโโโโโโโโ โ
โ โ โ โ โ
โ โโโโโโโโโโผโโโโโโโโโโโ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโ โ
โ โ SQLite โ โ
โ โ TodoTable โ โ
โ โโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโ โ
โ โ TUI Sidebar โ โ ๅฎๆถ่ฎข้
ๆพ็คบ โ
โ โ (sidebar.tsx)โ โ
โ โโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆ ธๅฟ่ฎพ่ฎกๅๅ๏ผ
- ๆ็ฎๆฅๅฃ๏ผไป ไธคไธชๅทฅๅ ท๏ผread/write๏ผ๏ผๆ ๅข้ๆดๆฐ
- ๅ จ้ๆฟๆข๏ผๆฏๆฌกๆดๆฐไผ ๅ ฅๅฎๆดๅ่กจ๏ผ็ฎๅๅนถๅๆงๅถ
- Session ้็ฆป๏ผๆฏไธชไผ่ฏ็ฌ็ซๅญๅจ๏ผๅคฉ็ถๆฏๆๅคไผ่ฏ
- ไบไปถ้ฉฑๅจ๏ผ้่ฟ Bus ไบไปถๅฎ็ฐ UI ๅฎๆถๅๆญฅ
โ ๏ธ ้่ฆๅบๅซ๏ผTodo ๅทฅๅ ท vs Plan ๆจกๅผ
็นๆง Todo ๅทฅๅ ท Plan ๆจกๅผ ่งฆๅๆนๅผ todowriteๅทฅๅ ท่ฐ็จplan_enterๅทฅๅ ท่ฐ็จAgent ๅๆข โ ไธๅๆข๏ผๅฝๅ agent ็ปง็ปญไฝฟ็จ โ ไผๅๆข build โ plan ๅทฅๅ ทๆ้ๅๅ ๆ ๅๅ Plan agent ้ๅถ็ผ่พๆ้ ็ณป็ปๆ้ ๆ ๆณจๅ ฅ PLAN_MODE_REMINDER็ฎ็ ไปปๅก่ท่ธช็ฎก็ ่ฟๅ ฅๅช่ฏป่งๅ้ถๆฎต ๅ ณ้ฎ็่งฃ๏ผTodo ๆฏๅไธ Agent ๅ ็ไปปๅก็ฎก็ๅทฅๅ ท๏ผPlan ๆฏไธๅ Agent ไน้ด็ๆจกๅผๅๆขใ
2. ๆฐๆฎๆจกๅ
2.1 TypeScript ๅฎไน
ๆไปถ: packages/opencode/src/session/todo.ts
export namespace Todo {
// ๆ ธๅฟๆฐๆฎๆจกๅ - ๅชๆ 3 ไธชๅญๆฎต
export const Info = z
.object({
content: z.string().describe("Brief description of the task"),
status: z.string().describe("Current status: pending, in_progress, completed, cancelled"),
priority: z.string().describe("Priority level: high, medium, low"),
})
.meta({ ref: "Todo" })
export type Info = z.infer<typeof Info>
}2.2 ๅญๆฎต่ฏดๆ
| ๅญๆฎต | ็ฑปๅ | ่ฏดๆ | ็คบไพๅผ |
|---|---|---|---|
content | string | ไปปๅกๆ่ฟฐ | โImplement user authenticationโ |
status | string | ไปปๅก็ถๆ | "pending", "in_progress", "completed", "cancelled" |
priority | string | ไผๅ ็บง | "high", "medium", "low" |
ไธบไปไนๆฒกๆๆดๅคๅญๆฎต๏ผ
- ๆฒกๆ
id๏ผ็จๆฐ็ป็ดขๅผ๏ผposition๏ผๆ ่ฏ - ๆฒกๆ
created_at/updated_at๏ผ็ฎๅ่ฎพ่ฎก๏ผไธ้่ฆๆถ้ด่ฟฝ่ธช - ๆฒกๆ
assignee๏ผๅ็จๆทๅบๆฏ๏ผๆ ้ๅ้
3. ๅญๅจๅฑๅฎ็ฐ
3.1 ๆฐๆฎๅบ่กจ็ปๆ
ๆไปถ: packages/opencode/src/session/session.sql.ts
export const TodoTable = sqliteTable(
"todo",
{
session_id: text()
.notNull()
.references(() => SessionTable.id, { onDelete: "cascade" }),
content: text().notNull(),
status: text().notNull(), // pending | in_progress | completed | cancelled
priority: text().notNull(), // high | medium | low
position: integer().notNull(), // ็จไบๆๅบ
},
(table) => [
// ๅคๅไธป้ฎ๏ผ(session_id, position)
primaryKey({ columns: [table.session_id, table.position] }),
// ็ดขๅผๅ ้ๆฅ่ฏข
index("todo_session_idx").on(table.session_id),
],
)่ฎพ่ฎก็น็น๏ผ
| ็นๆง | ่ฏดๆ | ไผๅฟ |
|---|---|---|
| Session ็บง่ๅ ้ค | onDelete: "cascade" | ๅ ้ค session ่ชๅจๆธ ็ todos |
| ๅคๅไธป้ฎ | (session_id, position) | ็กฎไฟๆฏไธช session ๅ ้กบๅบๅฏไธ |
| position ๅญๆฎต | ๆฐ็ป็ดขๅผ | ๆฏๆๆๅบ๏ผๆ ้้ขๅค็ order ๅญๆฎต |
3.2 ๅญๅจๆไฝ
ๆไปถ: packages/opencode/src/session/todo.ts
export namespace Todo {
// ๅ
จ้ๆฟๆขๆดๆฐ - ๅ
ณ้ฎ่ฎพ่ฎก๏ผ
export function update(input: { sessionID: string; todos: Info[] }) {
Database.transaction((db) => {
// 1. ๅ ้ค่ฏฅ session ็ๆๆ todos
db.delete(TodoTable)
.where(eq(TodoTable.session_id, input.sessionID))
.run()
if (input.todos.length === 0) return
// 2. ๆๅ
ฅๆฐๅ่กจ๏ผposition ๅญๆฎตๆงๅถ้กบๅบ๏ผ
db.insert(TodoTable)
.values(
input.todos.map((todo, position) => ({
session_id: input.sessionID,
content: todo.content,
status: todo.status,
priority: todo.priority,
position,
})),
)
.run()
})
// 3. ๅๅธๆดๆฐไบไปถ๏ผTUI ่ฎข้
ๆญคไบไปถ
Bus.publish(Event.Updated, input)
}
// ๆฅ่ฏข - ๆ position ๆๅบ
export function get(sessionID: string) {
const rows = Database.use((db) =>
db.select()
.from(TodoTable)
.where(eq(TodoTable.session_id, sessionID))
.orderBy(asc(TodoTable.position))
.all(),
)
return rows.map((row) => ({
content: row.content,
status: row.status,
priority: row.priority,
}))
}
}ไธบไปไน็จๅ จ้ๆฟๆข๏ผ
- ็ฎๅๅนถๅ๏ผ้ฟๅ ๅคๆ็ merge ้ป่พ
- AI ๅๅฅฝ๏ผLLM ็ๆๅฎๆดๅ่กจๆฏ่ฎก็ฎ diff ๆดๅฎนๆ
- ๅน็ญๆง๏ผๅคๆฌกๆง่ก็ธๅ่ฏทๆฑ็ปๆไธ่ด
- ไบๅกๅฎๅ จ๏ผๅๆฌกไบๅกๅฎๆ๏ผไธไผ้จๅๆดๆฐ
4. ๅทฅๅ ทๅฑๅฎ็ฐ
4.0 Todo vs Plan๏ผๅ ณ้ฎๅบๅซ
ๅพๅคๅผๅ่ ๅฎนๆๆททๆท Todo ๅทฅๅ ท ๅ Plan ๆจกๅผ๏ผๅฎไปฌๆฏๅฎๅ จไธๅ็ๆบๅถ๏ผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ไธค็งๅคๆๅบฆ็ฎก็ๆนๅผๅฏนๆฏ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Plan ๆจกๅผ (Agent ๅๆข) โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โข ่งฆๅ๏ผ่ฐ็จ plan_enter ๅทฅๅ
ท โ โ
โ โ โข ็ปๆ๏ผๅๆขๅฐ Plan Agent โ โ
โ โ โข ๆ้๏ผ่ฟๅ
ฅๅช่ฏปๆจกๅผ๏ผไธ่ฝ็ผ่พไปฃ็ ๆไปถ โ โ
โ โ โข ็ฎ็๏ผ่ฐ็ ๅๆ๏ผๅถๅฎ่งๅ๏ผๅๅ
ฅ .dm_cc/plans/*.md โ โ
โ โ โข ้ๅบ๏ผ่ฐ็จ plan_exit ๅๆขๅ Build Agent โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Todo ๅทฅๅ
ท (ๅไธ Agent ๅ
) โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โข ่งฆๅ๏ผ่ฐ็จ todowrite ๅทฅๅ
ท โ โ
โ โ โข ็ปๆ๏ผๅฝๅ Agent ็ปง็ปญๆง่ก๏ผไธๅๆข๏ผ โ โ
โ โ โข ๆ้๏ผๆ ๅๅ๏ผBuild Agent ไปๅฏไฝฟ็จๆๆๅทฅๅ
ท โ โ
โ โ โข ็ฎ็๏ผ่ท่ธชไปปๅก่ฟๅบฆ๏ผๆ ่ฎฐๅฎๆ็ถๆ โ โ
โ โ โข ๅญๅจ๏ผ.dm_cc/todos/{session_id}.json โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๅณ็ญๆต็จ๏ผ
็จๆท่ฏทๆฑๅคๆไปปๅก
โ
โโโ ้่ฆๅ
่ฐ็ /่งๅ๏ผโโโโบ ่ฐ็จ plan_enter โโโบ ่ฟๅ
ฅ Plan Agent
โ (ๅถๅฎๆดไฝๆนๆก)
โ
โโโ ็ดๆฅๆง่กๅคๆญฅ้ชคไปปๅก๏ผโโโบ ่ฐ็จ todowrite โโโบ ไฟๆ Build Agent
(่ท่ธชๆง่ก่ฟๅบฆ)
4.1 ๅทฅๅ ทๅฎไน
ๆไปถ: packages/opencode/src/tool/todo.ts
export const TodoWriteTool = Tool.define("todowrite", {
description: DESCRIPTION_WRITE, // ่ฏฆ็ป็ few-shot ๆ่ฟฐ
parameters: z.object({
todos: z.array(z.object(Todo.Info.shape))
.describe("The updated todo list"),
}),
async execute(params, ctx) {
// ๆ้ๆฃๆฅ๏ผๅฏ้๏ผๅ ไธบ always: ["*"]๏ผ
await ctx.ask({
permission: "todowrite",
patterns: ["*"],
always: ["*"],
metadata: {},
})
await Todo.update({
sessionID: ctx.sessionID,
todos: params.todos,
})
return {
title: `${params.todos.filter((x) => x.status !== "completed").length} todos`,
output: JSON.stringify(params.todos, null, 2),
metadata: { todos: params.todos },
}
},
})
export const TodoReadTool = Tool.define("todoread", {
description: "Use this tool to read your todo list",
parameters: z.object({}), // ๆ ๅๆฐ
async execute(_params, ctx) {
await ctx.ask({
permission: "todoread",
patterns: ["*"],
always: ["*"],
metadata: {},
})
const todos = await Todo.get(ctx.sessionID)
return {
title: `${todos.filter((x) => x.status !== "completed").length} todos`,
metadata: { todos },
output: JSON.stringify(todos, null, 2),
}
},
})4.2 ๅทฅๅ ทๅๆฐๅฏนๆฏ
| ๅทฅๅ ท | ๅๆฐ | ่ฟๅๅผ |
|---|---|---|
todoread | ๆ ๏ผ็ฉบๅฏน่ฑก๏ผ | { title, output, metadata: { todos } } |
todowrite | { todos: Todo.Info[] } | { title, output, metadata: { todos } } |
title ็่ฎก็ฎ๏ผpending ็ถๆ็ไปปๅกๆฐ้
title: `${todos.filter((x) => x.status !== "completed").length} todos`4.3 ๅทฅๅ ทๆ่ฟฐๆไปถ
ๆไปถ: packages/opencode/src/tool/todowrite.txt๏ผ่้๏ผ
Use this tool to create and manage a structured task list for your current coding session.
This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
## When to Use This Tool
Use this tool proactively in these scenarios:
1. Complex multistep tasks - When a task requires 3 or more distinct steps or actions
2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
3. User explicitly requests todo list - When the user directly asks you to use the todo list
4. User provides multiple tasks - When users provide a list of things to be done
5. After receiving new instructions - Immediately capture user requirements as todos
6. After completing a task - Mark it complete and add any new follow-up tasks
7. When you start working on a new task, mark the todo as in_progress
## When NOT to Use This Tool
Skip using this tool when:
1. There is only a single, straightforward task
2. The task is trivial and tracking it provides no organizational benefit
3. The task can be completed in less than 3 trivial steps
4. The task is purely conversational or informational
## Task States and Management
1. **Task States**:
- pending: Task not yet started
- in_progress: Currently working on (limit to ONE task at a time)
- completed: Task finished successfully
- cancelled: Task no longer needed
2. **Task Management**:
- Update task status in real-time as you work
- Mark tasks complete IMMEDIATELY after finishing
- Only have ONE task in_progress at any time
- Complete current tasks before starting new ones
4.4 ่งฆๅๆกไปถๅๆ๏ผไป Prompt ๆๅ๏ผ
ไปไนๆถๅ่ฐ็จ todowrite๏ผ โโ ๅฎๅ จ็ฑ Prompt ่งๅๅณๅฎ๏ผไธๆฏไปฃ็ ็กฌ็ผ็ ๏ผ
| ๅบๆฏ | ่งฆๅๆกไปถ | ็คบไพ |
|---|---|---|
| โ ๅคๆๅคๆญฅ้ชค | ไปปๅก้่ฆ 3+ ไธชไธๅๆญฅ้ชค | ๅฎ็ฐๆ้ปๆจกๅผ๏ผUI + ็ถๆ็ฎก็ + ๆ ทๅผ๏ผ |
| โ ็จๆทๆ็กฎ่ฆๆฑ | ็จๆท่ฏด โcreate a todo list" | "ๅธฎๆๅๅปบไธไธชไปปๅกๅ่กจ่ท่ธช่ฟๅบฆโ |
| โ ๅคไธชไปปๅก | ็จๆทๆไพๅ่กจ๏ผ้ๅท/็ผๅทๅ้๏ผ | โๅฎ็ฐ A, B, C ไธไธชๅ่ฝโ |
| โ ๆฐๆไปค | ๆถๅฐๆฐ้ๆฑๆถ็ซๅณๆ่ท | ็จๆทไธญ้่ฟฝๅ ้ๆฑ |
| โ ๅผๅงๆฐไปปๅก | ๅฐๆฐไปปๅกๆ ่ฎฐไธบ in_progress | ๅฎๆ็ฌฌไธๆญฅ๏ผๅผๅง็ฌฌไบๆญฅ |
| โ ็ฎๅไปปๅก | < 3 ไธชๆญฅ้ชค๏ผ็ดๆฅๅฎๆ | โprint Hello Worldโ |
| โ ็บฏๅฏน่ฏ | ไฟกๆฏๆฅ่ฏข๏ผๆ ๅฎ้ ๆไฝ | โgit status ๆฏๅไปไน็๏ผโ |
ๅ ณ้ฎ็่งฃ๏ผ
- ไฝฟ็จ Todo ไธไผ่งฆๅไปปไฝ็ถๆๅๅๆ Agent ๅๆข
- ๅชๆฏๆฎ้็ๅทฅๅ ท่ฐ็จ๏ผๆง่กๅ่ฟๅ็ปๆ๏ผ็ปง็ปญๅฝๅ Agent ็ๅพช็ฏ
- ไธ Plan ๆจกๅผ็ๅบๅซ๏ผPlan ไผๅๆข Agent ๅนถๆนๅ็ณป็ป prompt
5. ๅฎ้ ไฝฟ็จ็คบไพ
5.1 ็คบไพ 1: ๆทปๅ ๆ้ปๆจกๅผ
็จๆท: โI want to add a dark mode toggle to the application settings. Make sure you run the tests and build when youโre done!โ
AI ๆง่กๆต็จ:
// Step 1: ๅๅปบ todo ๅ่กจ
await todowrite({
todos: [
{ content: "Create dark mode toggle component in Settings page", status: "pending", priority: "high" },
{ content: "Add dark mode state management (context/store)", status: "pending", priority: "high" },
{ content: "Implement CSS-in-JS styles for dark theme", status: "pending", priority: "medium" },
{ content: "Update existing components to support theme switching", status: "pending", priority: "medium" },
{ content: "Run tests and build process", status: "pending", priority: "high" },
]
})
// Step 2: ๅผๅง็ฌฌไธไธชไปปๅก
await todowrite({
todos: [
{ content: "Create dark mode toggle component in Settings page", status: "in_progress", priority: "high" },
// ... ๅ
ถไปไฟๆไธๅ
]
})
// Step 3: ๅฎๆ็ฌฌไธไธชไปปๅก๏ผๅผๅง็ฌฌไบไธช
await todowrite({
todos: [
{ content: "Create dark mode toggle component in Settings page", status: "completed", priority: "high" },
{ content: "Add dark mode state management (context/store)", status: "in_progress", priority: "high" },
// ... ๅ
ถไปไฟๆไธๅ
]
})5.2 ็คบไพ 2: ้ๆๅฝๆฐๅ
็จๆท: โHelp me rename the function getCwd to getCurrentWorkingDirectory across my projectโ
AI ๆง่กๆต็จ:
// Step 1: ๅ
ๆ็ดขไบ่งฃๆ
ๅต
const results = await search({ pattern: "getCwd" })
// ๅ็ฐ 15 ๅค่ฐ็จ๏ผๅๅธๅจ 8 ไธชๆไปถไธญ
// Step 2: ๅๅปบ todo ๅ่กจ
await todowrite({
todos: [
{ content: "Update getCwd in src/utils/path.ts", status: "pending", priority: "high" },
{ content: "Update getCwd in src/config/loader.ts", status: "pending", priority: "high" },
{ content: "Update getCwd in src/cli/commands.ts", status: "pending", priority: "high" },
{ content: "Update getCwd in tests/path.test.ts", status: "pending", priority: "medium" },
// ... ๅ
ถไปๆไปถ
]
})
// Step 3: ้ไธชๅค็...5.3 ็คบไพ 3: ่ฏปๅๅนถๆดๆฐ
// ่ฏปๅๅฝๅๅ่กจ
const { metadata } = await todoread({})
const currentTodos = metadata.todos
// ๆทปๅ ๆฐไปปๅก
await todowrite({
todos: [
...currentTodos,
{ content: "New follow-up task", status: "pending", priority: "medium" }
]
})6. UI ๅฑ็คบ
6.1 TUI ไพง่พนๆ ๅฑ็คบ
ๆไปถ: packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx
<Show when={todo().length > 0 && todo().some((t) => t.status !== "completed")}>
<box>
<box flexDirection="row">
<text><b>Todo</b></text>
</box>
<For each={todo()}>
{(todo) => <TodoItem status={todo.status} content={todo.content} />}
</For>
</box>
</Show>ๆพ็คบๆกไปถ๏ผ
- ๆ todo ้กน
- ่ณๅฐๆไธไธชๆชๅฎๆ็ไปปๅก
6.2 Todo ้กน็ปไปถ
ๆไปถ: packages/opencode/src/cli/cmd/tui/component/todo-item.tsx
export function TodoItem(props: { status: string; content: string }) {
return (
<box flexDirection="row">
{/* ็ถๆๅพๆ */}
<text style={{
fg: props.status === "in_progress" ? theme.warning : theme.textMuted,
}}>
[{props.status === "completed" ? "โ" :
props.status === "in_progress" ? "โข" : " "}] {" "}
</text>
{/* ไปปๅกๅ
ๅฎน */}
<text style={{
fg: props.status === "in_progress" ? theme.warning : theme.textMuted,
}}>
{props.content}
</text>
</box>
)
}6.3 UI ๆๆ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Session: ๅฎ็ฐ็ตๅ็ฝ็ซ โ
โ โ
โ Todo โ
โ [โข] ่ฎพ่ฎกๅๅๅฑ็คบ้กต้ข โ in_progress โ
โ [ ] ๅฎ็ฐ่ดญ็ฉ่ฝฆๅ่ฝ โ pending โ
โ [ ] ๅผๅ่ฎขๅ็ฎก็็ณป็ป โ
โ [โ] ๅๅงๅ้กน็ฎ็ปๆ โ completed โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโ โ
โ Cost: $0.023 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
่ง่ง่ฎพ่ฎก๏ผ
โ- ๅทฒๅฎๆ๏ผ็ฐ่ฒ/ๆๅ๏ผโข- ่ฟ่กไธญ๏ผ้ป่ฒ/่ญฆๅ่ฒ๏ผ็ชๅบๆพ็คบ๏ผ- ๅพ ๅค็๏ผ็ฐ่ฒ๏ผ
7. ไธ dm_cc ็ๅฏนๆฏ
7.1 ๆถๆๅฏนๆฏ
| ็นๆง | opencode (TypeScript) | dm_cc ๅปบ่ฎฎ (Python) |
|---|---|---|
| ๅญๅจ | SQLite + Drizzle | JSON ๆไปถ / SQLite |
| ไบไปถๆป็บฟ | ่ชๅฎไน Bus | ๆ ้๏ผๆ TUI๏ผ |
| UI ๆกๆถ | Ink (React-like) | ๆ / Rich Console |
| ๆ้ | PermissionNext ่งๅ | AgentConfig ๅทฅๅ ท่ฟๆปค |
| ๆดๆฐ็ญ็ฅ | ๅ จ้ๆฟๆข | ๅ จ้ๆฟๆข๏ผไฟๆไธ่ด๏ผ |
7.2 ไปฃ็ ๅฏนๆฏ
ๆฐๆฎๆจกๅ:
// opencode - TypeScript
export const Info = z.object({
content: z.string(),
status: z.string(), // pending | in_progress | completed | cancelled
priority: z.string(), // high | medium | low
})# dm_cc - Python ๅปบ่ฎฎ
@dataclass
class TodoItem:
content: str
status: Literal["pending", "in_progress", "completed", "cancelled"]
priority: Literal["high", "medium", "low"]ๅญๅจ:
// opencode - SQLite
export function update(input: { sessionID: string; todos: Info[] }) {
Database.transaction((db) => {
db.delete(TodoTable).where(eq(TodoTable.session_id, input.sessionID)).run()
if (input.todos.length === 0) return
db.insert(TodoTable).values(...).run()
})
Bus.publish(Event.Updated, input)
}# dm_cc - JSON ๆไปถๅปบ่ฎฎ
class TodoStore:
def update(self, todos: list[TodoItem]) -> None:
"""ๅ
จ้ๆฟๆขๆดๆฐ"""
data = [todo.to_dict() for todo in todos]
self._file_path.write_text(
json.dumps(data, indent=2, ensure_ascii=False)
)8. ่ฎพ่ฎก่ฆ็นๆป็ป
8.1 ๅ ณ้ฎ่ฎพ่ฎกๅณ็ญ
| ๅณ็ญ | ้ๆฉ | ็็ฑ |
|---|---|---|
| ๆดๆฐ็ญ็ฅ | ๅ จ้ๆฟๆข | ้ฟๅ ๅคๆๅนถๅๆงๅถ๏ผAI ๅๅฅฝ |
8.2 Agent ๆ้้ ็ฝฎ
ๆไปถ: packages/opencode/src/agent/agent.ts
const result: Record<string, Info> = {
build: {
name: "build",
// Build agent ้ป่ฎคๅฏ็จๆๆๅทฅๅ
ท๏ผๅ
ๆฌ todowrite/todoread
permission: PermissionNext.merge(defaults, {
question: "allow",
plan_enter: "allow",
// todowrite/todoread ้ป่ฎคๅฏ็จ๏ผๅจ defaults ไธญ "*": "allow"๏ผ
}),
mode: "primary",
},
plan: {
name: "plan",
// Plan agent ไนๅฏไปฅไฝฟ็จ todo ๅทฅๅ
ท
permission: PermissionNext.merge(defaults, {
plan_exit: "allow",
// ๆฒกๆ็ฆ็จ todo๏ผ่งๅๆถไน้่ฆไปปๅก็ฎก็
}),
mode: "primary",
},
general: {
name: "general",
// Subagent - ๆพๅผ็ฆ็จ todo๏ผ
permission: PermissionNext.merge(defaults, {
todoread: "deny", // โ ็ฆๆญข
todowrite: "deny", // โ ็ฆๆญข
}),
mode: "subagent",
},
explore: {
name: "explore",
// Explore subagent ไน็ฆ็จ todo
permission: PermissionNext.merge(defaults, {
todoread: "deny",
todowrite: "deny",
}),
mode: "subagent",
},
}ไธบไปไน Subagent ็ฆ็จ Todo๏ผ
- Subagent๏ผgeneral/explore๏ผๅชๆง่กๅไธไปปๅก
- ไธๅบ่ฎฟ้ฎๆไฟฎๆน็ถไผ่ฏ็ todo ๅ่กจ
- ไฟๆๅ ณๆณจ็นๅ็ฆป๏ผ้ฟๅ ๅญไปปๅกๅนฒๆฐไธปไปปๅก่ท่ธช
8.3 Todo ไธ Plan ็ๅฎๆดๅฏนๆฏ
| ็ปดๅบฆ | Todo ๅทฅๅ ท | Plan ๆจกๅผ |
|---|---|---|
| ่งฆๅๆนๅผ | todowrite ๅทฅๅ
ท่ฐ็จ | plan_enter ๅทฅๅ
ท่ฐ็จ |
| Agent ๅๆข | โ ๅฆ๏ผไฟๆๅฝๅ Agent | โ ๆฏ๏ผbuild โ plan |
| ๆ้ๅๅ | ๆ | Plan agent ้ๅถ็ผ่พๆ้ |
| ็ณป็ป Prompt | ไธๅ | ๆณจๅ
ฅ PLAN_MODE_REMINDER |
| ็ฎ็ | ่ท่ธชไปปๅกๆง่ก่ฟๅบฆ | ่ฟๅ ฅๅช่ฏป่งๅ้ถๆฎต |
| ๅญๅจไฝ็ฝฎ | .dm_cc/todos/{session_id}.json | .dm_cc/plans/*.md |
| ้็จ Agent | Build, Plan | Build ่ฐ็จ๏ผPlan ๆง่ก |
| Subagent ๆ้ | โ general/explore ็ฆ็จ | โ general/explore ็ฆ็จ |
| ๅๆฐ่ฎพ่ฎก | ๆ็ฎ๏ผtodos ๆฐ็ป๏ผ | LLM ๅฎนๆ็ๆๅฎๆดๅ่กจ |
| ๅญๅจ | SQLite + ORM | ่ฝป้ใไบๅกๆฏๆใๆไบๆฅ่ฏข |
| ไฝ็จๅ | Session ็บงๅซ | ่ช็ถ้็ฆป๏ผๅคไผ่ฏไธๅฒ็ช |
| ๅๆญฅๆบๅถ | Bus ไบไปถๅๅธ | ๅฎๆถ UI ๆดๆฐ๏ผๆพ่ฆๅ |
8.2 ๆ้ๆงๅถ
opencode ไธญ Todo ๅทฅๅ ทไฝฟ็จๆ ๅๆ้็ณป็ป๏ผSubagent ็ฆๆญขไฝฟ็จ๏ผ
// general/explore subagent ้
็ฝฎ
{
name: "general",
permission: PermissionNext.merge(
defaults,
PermissionNext.fromConfig({
todoread: "deny", // ็ฆๆญข่ฏปๅ todo
todowrite: "deny", // ็ฆๆญขไฟฎๆน todo
}),
),
}ไธบไปไน้ๅถ Subagent๏ผ
- Subagent๏ผๅฆ generalใexplore๏ผๅชๆง่กๅไธไปปๅก
- ไธๅบ่ฎฟ้ฎๆไฟฎๆน็ถไผ่ฏ็ todo ๅ่กจ
- ไฟๆๅ ณๆณจ็นๅ็ฆป
8.3 AI ไฝฟ็จๅบๆฏ
| ๅบๆฏ | ่งฆๅๆกไปถ | AI ๆไฝ |
|---|---|---|
| ๅๅปบ | ๅคๆญฅ้ชคไปปๅก๏ผ3+ ๆญฅ้ชค๏ผ | ่ฐ็จ todowrite ๅๅปบๅๅงๅ่กจ |
| ๆดๆฐ | ๅผๅงๆฐไปปๅก | ๆ ่ฎฐไธบ in_progress |
| ๅฎๆ | ไปปๅกๅฎๆ | ๆ ่ฎฐไธบ completed |
| ๆทปๅ | ๅ็ฐๆฐไปปๅก | ่ฏปๅๅฝๅๅ่กจ๏ผ่ฟฝๅ ๆฐไปปๅก |
| ๆฃๆฅ | ๆฏ้ 3-5 ่ฝฎๅฏน่ฏ | ่ฐ็จ todoread ็กฎ่ฎค่ฟๅบฆ |
8.4 ๅฎ็ฐๅปบ่ฎฎ๏ผdm_cc๏ผ
# ============ ๅญๅจๅฑ ============
class TodoStore:
"""Session ็บงๅซ็ Todo ๅญๅจ"""
def __init__(self, session_id: str):
self.session_id = session_id
self.file_path = TODOS_DIR / f"{session_id}.json"
def get_all(self) -> list[TodoItem]:
if not self.file_path.exists():
return []
data = json.loads(self.file_path.read_text())
return [TodoItem(**item) for item in data]
def update(self, todos: list[TodoItem]):
"""ๅ
จ้ๆฟๆขๆดๆฐ"""
data = [asdict(todo) for todo in todos]
self.file_path.write_text(json.dumps(data, indent=2))
# ============ ๅทฅๅ
ทๅฑ ============
class TodoReadTool(Tool):
name = "todo_read"
parameters = None # ๆ ๅๆฐ
async def execute(self, params) -> dict:
session_id = get_current_session_id()
store = TodoStore(session_id)
todos = store.get_all()
return {
"title": f"{len([t for t in todos if t.status != 'completed'])} todos",
"output": format_todos(todos),
"metadata": {"todos": todos},
}
class TodoWriteTool(Tool):
name = "todo_write"
class Parameters(BaseModel):
todos: list[TodoItem]
async def execute(self, params: Parameters) -> dict:
session_id = get_current_session_id()
store = TodoStore(session_id)
store.update(params.todos)
return {"title": "Updated", "output": "Todo list updated"}้ๅฝ๏ผๅฎๆดๆไปถๆธ ๅ
| ๆไปถ | ่ฏดๆ |
|---|---|
packages/opencode/src/session/todo.ts | ๆฐๆฎๆจกๅๅๅญๅจๆไฝ |
packages/opencode/src/session/session.sql.ts | ๆฐๆฎๅบ่กจๅฎไน |
packages/opencode/src/tool/todo.ts | ๅทฅๅ ทๅฎไน |
packages/opencode/src/tool/todoread.txt | ่ฏปๅๅทฅๅ ทๆ่ฟฐ |
packages/opencode/src/tool/todowrite.txt | ๅๅ ฅๅทฅๅ ทๆ่ฟฐ๏ผๅซ่งฆๅๆกไปถ๏ผ |
packages/opencode/src/agent/agent.ts | Agent ๆ้้ ็ฝฎ๏ผtodo ็ฆ็จ่งๅ๏ผ |
packages/opencode/src/cli/cmd/tui/component/todo-item.tsx | UI ็ปไปถ |
packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx | ไพง่พนๆ ๅฑ็คบ |