OpenCode 实践课:4 分钟用 Python 写出一个命令行 TODO 管理器

项目介绍

一个命令行 TODO 管理器,支持添加任务、设置优先级、标记完成、删除任务和列表过滤。数据存储在本地 JSON 文件中,无需数据库,开箱即用。

  • 技术栈:Python 3 + 标准库(argparse、json、datetime)
  • 代码量:约 110 行
  • 功能:增删改查 + 优先级标签 + 到期日期

准备工作

  • Python 3.8+
  • 安装 OpenCode(终端 AI 编程助手):npm install -g opencode

实践过程

第一步:创建项目骨架

我对 OpenCode 的提示词:

> 用 Python 写一个命令行 TODO 管理器,使用 argparse 处理命令,支持 addlistdonedelete 四个子命令,数据保存到 ~/.todo.json。只输出完整代码文件。

OpenCode 在几秒内生成了完整的 todo.py,包含 argparse 子命令解析、JSON 文件读写和四个基础命令的实现。我直接将代码保存为 todo.py 并运行:

python todo.py add "学习 OpenCode" --priority high
python todo.py list

输出:

[1] [HIGH] 学习 OpenCode

要点:一次提示词就拿到了可用原型,不需要手写 argparse 样板代码。

第二步:增加截止日期和列表过滤

我对 OpenCode 说:

> 任务需要支持截止日期 --due YYYY-MM-DD,list 命令加 --status pending|done|all 过滤。命令行示例:
> python todo.py add 写周报 --due 2026-08-10 --priority medium

OpenCode 更新了代码,在 add 子命令中新增 --due 参数,在 list 中新增 --status 参数,并用 datetime 模块解析日期:

parser_add.add_argument("--due", type=str, help="截止日期 YYYY-MM-DD")
parser_list.add_argument("--status", choices=["pending", "done", "all"], default="pending")

运行时效果:

python todo.py add "交项目报告" --due 2026-08-10 --priority high
python todo.py list
[1] [HIGH] 学习 OpenCode
[2] [HIGH] 交项目报告 — 截止: 2026-08-10

要点:我只需要描述想要的功能和参数名,OpenCode 自动处理了参数定义、日期解析和数据存储逻辑。

第三步:增加完成和删除功能

我对 OpenCode 说:

> done 命令用任务编号标记完成,并显示完成时间;delete 命令删除任务前需要确认。list 已完成的用 [✓] 标记。

OpenCode 增加了完成时间戳记录,删除前用 input() 请求二次确认,list 输出时对已完成任务加了视觉标记:

python todo.py done 1
python todo.py delete 2

输出:

✓ 任务 [1] 已完成
⚠ 确认删除任务 [2]「交项目报告」?(y/n): y
已删除任务 [2]

要点:交互式确认这类细节,同样只需要一句话描述。

第四步:打磨输出体验

我对 OpenCode 说:

> list 输出按优先级排序(high 在前),done 的任务显示完成时间,空列表显示友好提示。

OpenCode 调整了排序逻辑,增加了空列表判断和完成时间格式化:

python todo.py list --status all
📋 任务列表 (共 0 条)
暂无任务,用 `python todo.py add <任务>` 添加一条吧。

完整代码

import argparse
import json
import os
from datetime import datetime

TODO_FILE = os.path.expanduser("~/.todo.json")
PRIORITY_ORDER = {"high": 0, "medium": 1, "low": 2}


def load():
    if not os.path.exists(TODO_FILE):
        return []
    with open(TODO_FILE, "r", encoding="utf-8") as f:
        return json.load(f)


def save(tasks):
    with open(TODO_FILE, "w", encoding="utf-8") as f:
        json.dump(tasks, f, ensure_ascii=False, indent=2)


def cmd_add(args):
    tasks = load()
    task = {
        "id": len(tasks) + 1,
        "title": args.title,
        "priority": args.priority or "medium",
        "due": args.due,
        "done": False,
        "done_at": None,
        "created_at": datetime.now().strftime("%Y-%m-%d %H:%M"),
    }
    tasks.append(task)
    save(tasks)
    print(f"+ 已添加: [{task['id']}] {task['title']}")


def cmd_list(args):
    tasks = load()
    if args.status == "pending":
        tasks = [t for t in tasks if not t["done"]]
    elif args.status == "done":
        tasks = [t for t in tasks if t["done"]]

    tasks.sort(key=lambda t: PRIORITY_ORDER.get(t["priority"], 1))

    if not tasks:
        print("\n📋 任务列表 (共 0 条)")
        print('暂无任务,用 `python todo.py add <任务>` 添加一条吧。\n')
        return

    print(f"\n📋 任务列表 (共 {len(tasks)} 条)\n")
    for t in tasks:
        mark = "✓" if t["done"] else " "
        tag = {"high": "🔴", "medium": "🟡", "low": "🟢"}.get(t["priority"], "")
        due = f" — 截止: {t['due']}" if t.get("due") else ""
        done_info = ""
        if t["done"] and t.get("done_at"):
            done_info = f" (完成于 {t['done_at']})"
        print(f"  [{mark}] [{t['id']}] {tag} {t['title']}{due}{done_info}")
    print()


def cmd_done(args):
    tasks = load()
    for t in tasks:
        if t["id"] == args.id:
            t["done"] = True
            t["done_at"] = datetime.now().strftime("%Y-%m-%d %H:%M")
            save(tasks)
            print(f"✓ 任务 [{args.id}] 已完成")
            return
    print(f"✗ 未找到任务 [{args.id}]")


def cmd_delete(args):
    tasks = load()
    for i, t in enumerate(tasks):
        if t["id"] == args.id:
            confirm = input(f'⚠ 确认删除任务 [{args.id}]「{t["title"]}」?(y/n): ')
            if confirm.lower() == "y":
                del tasks[i]
                save(tasks)
                print(f"已删除任务 [{args.id}]")
            return
    print(f"✗ 未找到任务 [{args.id}]")


def main():
    parser = argparse.ArgumentParser(description="命令行 TODO 管理器")
    subs = parser.add_subparsers(dest="command", required=True)

    p_add = subs.add_parser("add", help="添加任务")
    p_add.add_argument("title", type=str, help="任务标题")
    p_add.add_argument("--priority", choices=["high", "medium", "low"], help="优先级")
    p_add.add_argument("--due", type=str, help="截止日期 YYYY-MM-DD")
    p_add.set_defaults(func=cmd_add)

    p_list = subs.add_parser("list", help="列出任务")
    p_list.add_argument("--status", choices=["pending", "done", "all"], default="pending")
    p_list.set_defaults(func=cmd_list)

    p_done = subs.add_parser("done", help="完成任务")
    p_done.add_argument("id", type=int, help="任务编号")
    p_done.set_defaults(func=cmd_done)

    p_del = subs.add_parser("delete", help="删除任务")
    p_del.add_argument("id", type=int, help="任务编号")
    p_del.set_defaults(func=cmd_delete)

    args = parser.parse_args()
    args.func(args)


if __name__ == "__main__":
    main()

代码共 110 行,包含完整的命令行 TODO 管理器。保存为 todo.py 即可使用:

python todo.py add "学 OpenCode" --priority high --due 2026-08-15
python todo.py add "写周报" --priority medium
python todo.py list
python todo.py done 1
python todo.py list --status all
python todo.py delete 2

小结

整个开发过程约 4 分钟,全程没有手写一行样板代码。用 OpenCode 开发这类小工具的效率提升体现在:

argparse 子命令配置 — 自己写需要反复查文档,OpenCode 一次生成正确

JSON 文件读写 — 加载/保存/空文件处理的边界情况自动覆盖

交互细节 — 排序、图标、空列表提示等体验打磨,零成本追加

和手写相比,省掉的不是打字时间,而是"边写边查文档、边写边调 bug"的心智开销。对于 100 行级别的小工具,OpenCode 的优势是让你始终专注在"要做什么"而不是"怎么做"。