今天我们用 OpenCode 从零开始,做一个实用的命令行 TODO 管理工具。技术栈用 Go 标准库就够了,最终代码约 110 行,支持增删改查和 JSON 文件持久化。
你不需要手写一行代码——全程用自然语言指挥 OpenCode 完成。我会把每一步的对话和 OpenCode 的响应都展示出来,让你直观感受这个工作流。
npm install -g opencode 或参考 opencode.ai 文档)我对 OpenCode 说:
> 帮我初始化一个 Go 项目,模块名叫 todo,创建一个 main.go 作为入口文件。
OpenCode 执行了:
mkdir todo && cd todo && go mod init todo
然后生成了一个最简的 main.go:
package main
import "fmt"
func main() {
fmt.Println("TODO Manager")
}
我只用一句话就让 OpenCode 完成了项目脚手架搭建,模块名、目录结构一并搞定。
我对 OpenCode 说:
> 现在增加 TODO 的数据结构,每条包含 ID、Title、Done、CreatedAt。然后实现从 tasks.json 加载和保存的函数,用 encoding/json 包。
OpenCode 先是问我——它没有直接写代码,而是确认了几个关键细节:
我回答"不存在就返回空列表 + 自动创建文件 / 自增 int / 用 time.Time"之后,OpenCode 一次性生成了完整的结构体和读写函数。这是我觉得很关键的一点:OpenCode 在动手前会主动澄清模糊需求,而不是闷头乱写。
我对 OpenCode 说:
> 现在实现 CLI 命令解析。用 flag 包,支持:
> -todo add "标题" 添加任务
> -todo list 列出所有任务
> -todo done 1 标记第 1 条为完成
> -todo del 1 删除第 1 条
OpenCode 生成了一段完整的命令分发逻辑。它把 os.Args 解析、子命令路由、错误处理都写好了。生成完后,我直接 go run . add "学 OpenCode" 测试——成功入库。
这里有个小细节:OpenCode 在 list 命令里自动给已完成的任务加了 [x] 前缀、未完成的加 [ ] 前缀,这个小设计我并没有提,它自己加的,体验很好。
我对 OpenCode 说:
> 运行 go run . del 0 会 panic,因为切片 index out of range。帮我加上输入校验,del 和 done 的 ID 必须 >=1 且 <= len(tasks)。
OpenCode 读了一遍代码,精准定位到了 tasks = append(tasks[:idx], tasks[idx+1:]...) 这一行,然后在前面加了边界检查。修完之后还主动提示我可以加一个 -h 参数打印帮助信息——我回了一句"好,加上",它立刻补完了。
整个过程不到 30 秒,比我自己翻文档快多了。
package main
import (
"encoding/json"
"fmt"
"os"
"strconv"
"time"
)
type Task struct {
ID int `json:"id"`
Title string `json:"title"`
Done bool `json:"done"`
CreatedAt time.Time `json:"created_at"`
}
func loadTasks(file string) ([]Task, error) {
data, err := os.ReadFile(file)
if err != nil {
if os.IsNotExist(err) {
return []Task{}, nil
}
return nil, err
}
var tasks []Task
if len(data) == 0 {
return tasks, nil
}
if err := json.Unmarshal(data, &tasks); err != nil {
return nil, err
}
return tasks, nil
}
func saveTasks(file string, tasks []Task) error {
data, err := json.MarshalIndent(tasks, "", " ")
if err != nil {
return err
}
return os.WriteFile(file, data, 0644)
}
func nextID(tasks []Task) int {
max := 0
for _, t := range tasks {
if t.ID > max {
max = t.ID
}
}
return max + 1
}
func main() {
const file = "tasks.json"
tasks, err := loadTasks(file)
if err != nil {
fmt.Println("加载失败:", err)
os.Exit(1)
}
if len(os.Args) < 2 {
printHelp()
return
}
switch os.Args[1] {
case "add":
if len(os.Args) < 3 {
fmt.Println("用法: todo add <标题>")
return
}
title := os.Args[2]
task := Task{
ID: nextID(tasks),
Title: title,
Done: false,
CreatedAt: time.Now(),
}
tasks = append(tasks, task)
if err := saveTasks(file, tasks); err != nil {
fmt.Println("保存失败:", err)
os.Exit(1)
}
fmt.Printf("已添加 #%d: %s\n", task.ID, task.Title)
case "list":
if len(tasks) == 0 {
fmt.Println("暂无任务")
return
}
for _, t := range tasks {
status := "[ ]"
if t.Done {
status = "[✓]"
}
fmt.Printf("%s #%d %s (%s)\n", status, t.ID, t.Title,
t.CreatedAt.Format("2006-01-02"))
}
case "done":
if len(os.Args) < 3 {
fmt.Println("用法: todo done <ID>")
return
}
id, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("ID 必须是数字")
return
}
found := false
for i, t := range tasks {
if t.ID == id {
tasks[i].Done = true
found = true
break
}
}
if !found {
fmt.Printf("任务 #%d 不存在\n", id)
return
}
saveTasks(file, tasks)
fmt.Printf("已完成 #%d\n", id)
case "del":
if len(os.Args) < 3 {
fmt.Println("用法: todo del <ID>")
return
}
id, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("ID 必须是数字")
return
}
idx := -1
for i, t := range tasks {
if t.ID == id {
idx = i
break
}
}
if idx == -1 {
fmt.Printf("任务 #%d 不存在\n", id)
return
}
tasks = append(tasks[:idx], tasks[idx+1:]...)
saveTasks(file, tasks)
fmt.Printf("已删除 #%d\n", id)
default:
printHelp()
}
}
func printHelp() {
fmt.Println(`TODO 管理器 - 用法:
todo add <标题> 添加任务
todo list 列出所有任务
todo done <ID> 标记完成
todo del <ID> 删除任务`)
}
这个 TODO 管理器从零到跑通,我实际花了大约 5 分钟,期间写了 0 行代码——只写了 5 句中文提示词。对比自己手写,同等规模的项目至少需要 15-20 分钟(查文档、调语法、修边界 case)。
用 OpenCode 最大的感受不是"它替我写代码",而是把思考和执行的边界重新划分了——我只需要想清楚"要什么",具体的实现细节交给它。中间它主动追问设计决策、自动帮我补了边界校验和帮助信息,这种交互感让我觉得它像一个即时的结对编程伙伴,而不是一个代码生成器。
如果你还没试过用自然语言做开发,不妨从这个小项目开始,打开终端跟 OpenCode 聊两句,感受一下这个工作流。