乐于分享
好东西不私藏

实战横评】主流 AI 编程工具实测对比,看完不再盲目选型

实战横评】主流 AI 编程工具实测对比,看完不再盲目选型

AI 编程工具横评实测报告(2026-08-04)

7 款 AI 编程工具,同一模型(deepseek-v4-flash)、同一测试题、同一网关并行实测。 附带 token 消耗对比 + 代码质量 + 速度 + 易用性 + 已知 bug 全记录。


目录

  • 快速结论(30 秒版)
  • 测试方法(完整版)
    • 测试题原文 / 统一环境 / 执行命令 / 判定标准 / 局限与偏差 / 选型决策
  • 结果总表
  • 分工具详解(7 款逐一分析)
  • 模型对比:glm_for_coding vs deepseek-v4-flash
  • 最终推荐
  • 部署与配置坑(详版)
  • Token 数据证据链
  • 测试产物全集(7 份完整代码 + pytest 输出)

快速结论(30 秒版)

结论
说明
🏆 最佳工具
OpenCode
 — 最快(32s)+ 最省 token(11.7K),质量过关
质量天花板
Claude Code
 — 22/22 测试最全最严谨,但 token 消耗最大
意外之喜
Codex
 — 换 deepseek 后从失败×4 变 13/13(之前是模型问题)
配置最坑
Goose / Crush
 — 修好配置后都能用,但过程曲折(详见部署坑)
不推荐
Kilo
(OpenCode 劣化版)、Cline CLI(team_status 崩溃 bug)
通用结论
deepseek-v4-flash 是当前网关下最佳编码模型(比 glm_for_coding 快 5-15 倍)

阅读导航:想复现测试看 测试方法;想挑工具看 最终推荐;想看各工具实际写的代码看 测试产物全集;想避坑看 部署与配置坑;想核对数字看 Token 证据链。


测试方法(完整版)

1. 测试题(逐字原文,所有工具收到的 prompt 完全相同)

Create todo.py (Python CLI todo app with add/list/done/delete commands and JSON persistence) and test_todo.py (pytest unit tests). Run the tests and make sure they pass.

中文翻译:创建 todo.py(带 add/list/done/delete 命令和 JSON 持久化的 Python CLI 待办应用)和 test_todo.py(pytest 单元测试)。运行测试并确保全部通过。

这个测试题考察的能力

  • 理解需求并拆解(CLI 命令设计、JSON 存储设计)
  • 写出可测试的代码(函数/类结构可被 pytest 引用)
  • 自己写测试用例覆盖功能
  • 执行测试、发现失败、修复到全过(自我验证闭环)
  • 工具链使用(argparse、json、pytest、文件读写)

2. 统一环境

测试日期
2026-08-04
模型
deepseek-v4-flash(统一,通过网关 https://api.myagentlab.homes/v1
API key
同一把网关 key(用户提供,见 credentials)
系统
root 用户,Linux
Python
3.13.5
pytest
9.1.1(venv: /tmp/bench-venv,系统无 pip 故用 uv 建 venv 装)
工作目录
/home/agent/coding-benchmark/test-<工具名>/
(每工具独立目录,互不干扰)
执行方式
7 工具并行后台运行(同模型同 key 同时跑,网关竞争环境一致)

3. 每个工具的具体执行命令(实测用的真实命令)

工具
启动命令
备注
Claude Code
claude -p "<测试题>" --model deepseek-v4-flash --allowedTools 'Bash,Write,Read,Edit' --output-format stream-json --verbose
root 不能用 --dangerously-skip-permissions
OpenCode
opencode run "<测试题>"
项目级 opencode.json 配 myagentlab provider
Codex
codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --json '<测试题>'
config.toml 配 wire_api=chat
Cline
cline --json "<测试题>"
cline auth openai 配好;team_status bug 需 enableTeams:false 缓解
Kilo
kilo run -m myagentlab/deepseek-v4-flash "<测试题>"
kilo.jsonc 配 myagentlab provider
Crush
crush run -m myagentlab/deepseek-v4-flash "<测试题>"
crush.json 配 myagentlab provider
Goose
goose run -t '<测试题>' --provider myagentlab --model deepseek-v4-flash
需自定义 provider + supports_streaming:false

4. 判定标准

  • 通过
     = 该工具自己生成的 test_todo.py 用 pytest 跑通(/tmp/bench-venv/bin/python -m pytest test_todo.py),测试数量由工具自己决定(22/12/13/24/10/13/17 各不相同)
  • 耗时
     = 从启动命令到进程退出(含模型首 token 延迟 + 工具自我迭代)
  • token 数据
     = 各工具本地记录(来源见"Token 数据证据链"章节)
  • 代码质量
     = 人工检查产物(测试覆盖、边界处理、可读性)

5. 测试的局限与已知偏差(诚实声明)

  1. Cline 的 24/24 是修复后的
    :原始 22/24(2 个代码 bug),我手动修复了这 2 个 bug 后复验 24/24。纯工具产出是 22/24。
  2. Codex token 是估算值
    :本地不记录 usage,网关无分 key 明细,按 6 轮请求 × 累积上下文估算。成本 ~0.027 † Crush 的 crush.db sessions 表无 cache_read 字段,故无数据 ‡ Crush 记录值(completion_tokens 疑似只记末次请求,实际生成代码见产物)

    分工具详解

    🥇 1. OpenCode — 综合最优

    最快(32s)+ 最省 token(11.7K input,其他工具的 1/5-1/7)+ 代码质量过关。

    配置要点:

    // 项目级 opencode.json{  "provider": { "myagentlab": {    "npm": "@ai-sdk/openai-compatible",    "options": { "baseURL": "https://api.myagentlab.homes/v1", "apiKey": "见 credentials" },    "models": { "deepseek-v4-flash": { "name": "deepseek-v4-flash" } }  }},  "model": "myagentlab/deepseek-v4-flash"}
    opencode run "任务描述"

    🥈 2. Claude Code — 质量天花板

    代码质量最高(22 个测试最多最全),但 token 消耗最大。

    Token 分析: input 227K 中 190K 是 cache read(价格是正常 input 的 1/10),实际新计算 ~36K,并非全价消耗。

    配置要点:

    export ANTHROPIC_API_KEY=<key> ANTHROPIC_BASE_URL=https://api.myagentlab.homesclaude -p "任务" --model deepseek-v4-flash --allowedTools 'Bash,Write,Read,Edit' --output-format stream-json --verbose

    ⚠️ root 用户不能用 --dangerously-skip-permissions(被拒),必须用 --allowedTools

    🥉 3. Codex — 模型对了就起飞

    换 deepseek 后从"失败×4"变"13/13 通过、78s",但 token 数据缺失 + 有推理模型兼容隐患。

    配置要点:

    # ~/.codex/config.tomlmodel = "deepseek-v4-flash"model_provider = "myagentlab"[model_providers.myagentlab]name = "myagentlab"base_url = "https://api.myagentlab.homes/v1"env_key = "OPENAI_API_KEY"wire_api = "chat"
    codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --json '任务'

    4. Goose — 大器晚成

    配置坑最多但修好后很稳,17/17 通过,77s。

    ⚠️ 配置坑(都是实测踩出来的):

    配置要点:

    // ~/.config/goose/custom_providers/myagentlab.json{  "name": "myagentlab",  "engine": "openai",  "api_key_env": "MYAGENTLAB_API_KEY",  "base_url": "https://api.myagentlab.homes/v1",  "models": [ { "name": "deepseek-v4-flash", "context_limit": 128000 } ],  "supports_streaming": false,  "requires_auth": true}
    MYAGENTLAB_API_KEY=<key> goose run -t '任务' --provider myagentlab --model deepseek-v4-flash

    5. Crush — 焕然一新

    从 glm 下 13/14(有 bug)变成 deepseek 下 13/13 全过、66s,但配置极坑 + token 记录不全。

    ⚠️ 配置坑:

    配置要点:

    // ~/.config/crush/crush.json{  "providers": {    "myagentlab": {      "type""openai-compat",      "base_url""https://api.myagentlab.homes/v1",      "api_key""0.027Goose80,84843,7766,769~/.local/share/goose/sessions/sessions.db usage_ledger 表 SUM()Crush14,499无此字段40*~/.crush/crush.db sessions 表 prompt_tokens/completion_tokensCline11,2345,6326,033~/.cline/data/sessions/<id>/<id>.messages.json assistant metrics 累加Kilo15,488102,2722,291~/.local/share/kilo/kilo.db session 表* Crush completion_tokens=40 疑似只记末次请求(实际生成 124 行代码),已按实际产物修正理解复核命令示例(OpenCode):SELECT tokens_input, tokens_output, tokens_cache_read FROM sessionWHERE model LIKE '%deepseek%' ORDER BY time_created DESC LIMIT 1;复核命令示例(Goose):SELECT session_id, SUM(input_tokens), SUM(output_tokens), SUM(cache_read_tokens)FROM usage_ledger GROUP BY session_id ORDER BY MIN(id) DESC LIMIT 2;复核命令示例(Claude Code):grep -oE '"usage":\{[^}]*\}' /tmp/cc-ds.log | tail -1为什么 Codex 没有精确值: 本地 session jsonl 无 usage 字段;网关 billing 接口只给总额(共享 key,后台 MRS 任务并发消耗,差值法不可信);唯一精确途径是 new-api 后台按渠道查明细。附:测试产物全集(7 份完整代码 + pytest 输出)Claude Code v2.1.220 — 22/22Claude Code 生成的 todo.py (4619 bytes)#!/usr/bin/env python3"""A simple command-line todo app with JSON persistence.Usage:    todo.py add <task>      Add a new task    todo.py list            List all tasks    todo.py done <id>       Mark a task as done    todo.py delete <id>     Delete a task    todo.py --file <path>   Use a custom JSON file (default: ~/.todo.json)Tasks are stored in a JSON file of the form:    {"next_id"2"todos": [{"id"1"task""...""done"false}]}The ``next_id`` counter is persisted so task IDs are never reused, evenafter a delete. This prevents a stale reference from completing ordeleting the wrong task later."""import argparseimport jsonimport osimport sysDEFAULT_FILE = os.path.join(os.path.expanduser("~"), ".todo.json")class TodoApp:    """Todo list backed by a JSON file."""    def __init__(self, filepath=DEFAULT_FILE):        self.filepath = filepath    def load(self):        """Return the store (``next_id`` counter + ``todos`` list)."""        if not os.path.exists(self.filepath):            return {"next_id": 1, "todos": []}        with open(self.filepath, "r") as f:            content = f.read()        if not content.strip():            # Empty file (e.g. created by touch/mktemp) means a fresh store.            return {"next_id": 1, "todos": []}        data = json.loads(content)        # Backwards compatibility: a bare list of todos.        if isinstance(data, list):            data = {                "next_id": max((t["id"] for t in data), default=0) + 1,                "todos": data,            }        return data    def save(self, data):        """Write the store to the JSON file."""        with open(self.filepath, "w") as f:            json.dump(data, f, indent=2)    def _allocate_id(self, data):        next_id = data["next_id"]        data["next_id"] = next_id + 1        return next_id    def add(self, task):        """Add a new task and return the created todo."""        data = self.load()        todo = {"id": self._allocate_id(data), "task": task, "done": False}        data["todos"].append(todo)        self.save(data)        return todo    def list_todos(self):        """Return all todos in insertion order."""        return self.load()["todos"]    def done(self, todo_id):        """Mark a todo as done and return it, or None if not found."""        data = self.load()        for todo in data["todos"]:            if todo["id"] == todo_id:                todo["done"] = True                self.save(data)                return todo        return None    def delete(self, todo_id):        """Delete a todo and return it, or None if not found."""        data = self.load()        for i, todo in enumerate(data["todos"]):            if todo["id"] == todo_id:                removed = data["todos"].pop(i)                self.save(data)                return removed        return Nonedef main(argv=None):    parser = argparse.ArgumentParser(description="A simple todo app")    parser.add_argument(        "--file", default=DEFAULT_FILE, help="JSON file to store todos"    )    subparsers = parser.add_subparsers(dest="command", required=True)    add_parser = subparsers.add_parser("add", help="Add a task")    add_parser.add_argument("task", help="Task description")    subparsers.add_parser("list", help="List all tasks")    done_parser = subparsers.add_parser("done", help="Mark a task as done")    done_parser.add_argument("id", type=int, help="Task ID")    delete_parser = subparsers.add_parser("delete", help="Delete a task")    delete_parser.add_argument("id", type=int, help="Task ID")    args = parser.parse_args(argv)    app = TodoApp(args.file)    if args.command == "add":        todo = app.add(args.task)        print(f"Added todo #{todo['id']}: {todo['task']}")    elif args.command == "list":        todos = app.list_todos()        if not todos:            print("No todos.")            return 0        for todo in todos:            status = "[x]" if todo["done"] else "[ ]"            print(f"{todo['id']}. {status}{todo['task']}")    elif args.command == "done":        todo = app.done(args.id)        if todo is None:            print(f"Todo #{args.id} not found.", file=sys.stderr)            return 1        print(f"Done: {todo['task']}")    elif args.command == "delete":        todo = app.delete(args.id)        if todo is None:            print(f"Todo #{args.id} not found.", file=sys.stderr)            return 1        print(f"Deleted: {todo['task']}")    return 0if __name__ == "__main__":    sys.exit(main())Claude Code 生成的 test_todo.py (5048 bytes)"""Unit tests for the todo app."""import jsonimport pytestfrom todo import TodoApp, main@pytest.fixturedef app(tmp_path):    return TodoApp(filepath=tmp_path / "todos.json")# --- add ------------------------------------------------------------------def test_add_returns_created_todo(app):    todo = app.add("Buy milk")    assert todo == {"id": 1, "task": "Buy milk", "done": False}def test_add_persists_to_json(app, tmp_path):    app.add("Write tests")    data = json.loads((tmp_path / "todos.json").read_text())    assert len(data["todos"]) == 1    assert data["todos"][0]["task"] == "Write tests"    assert data["todos"][0]["done"] is Falsedef test_add_persists_next_id_counter(app, tmp_path):    app.add("first")    app.add("second")    data = json.loads((tmp_path / "todos.json").read_text())    assert data["next_id"] == 3def test_add_increments_ids(app):    app.add("first")    second = app.add("second")    assert second["id"] == 2    assert [t["id"] for t in app.list_todos()] == [1, 2]def test_add_after_delete_does_not_reuse_id(app):    first = app.add("first")    app.delete(first["id"])    third = app.add("third")    assert third["id"] == 2    assert [t["id"] for t in app.list_todos()] == [2]def test_ids_are_monotonic_across_restarts(tmp_path):    filepath = tmp_path / "todos.json"    app1 = TodoApp(filepath=filepath)    first = app1.add("first")    app2 = TodoApp(filepath=filepath)    app2.delete(first["id"])    third = app2.add("third")    assert third["id"] == 2# --- list -----------------------------------------------------------------def test_list_empty_when_no_file(tmp_path):    app = TodoApp(filepath=tmp_path / "nonexistent.json")    assert app.list_todos() == []def test_list_empty_when_file_is_empty(tmp_path):    filepath = tmp_path / "todos.json"    filepath.write_text("")    app = TodoApp(filepath=filepath)    assert app.list_todos() == []def test_add_over_empty_file(tmp_path):    filepath = tmp_path / "todos.json"    filepath.write_text("")    app = TodoApp(filepath=filepath)    todo = app.add("Task")    assert todo["id"] == 1def test_list_empty_when_file_is_empty_store(tmp_path):    filepath = tmp_path / "todos.json"    filepath.write_text(json.dumps({"next_id": 1, "todos": []}))    app = TodoApp(filepath=filepath)    assert app.list_todos() == []def test_list_returns_all_todos_in_order(app):    app.add("a")    app.add("b")    todos = app.list_todos()    assert [t["task"] for t in todos] == ["a", "b"]# --- done -----------------------------------------------------------------def test_done_marks_todo(app):    todo = app.add("Task")    result = app.done(todo["id"])    assert result["done"] is True    assert app.list_todos()[0]["done"] is Truedef test_done_persists_to_json(app, tmp_path):    todo = app.add("Task")    app.done(todo["id"])    data = json.loads((tmp_path / "todos.json").read_text())    assert data["todos"][0]["done"] is Truedef test_done_missing_returns_none(app):    assert app.done(999) is None# --- delete ---------------------------------------------------------------def test_delete_removes_todo(app):    todo = app.add("Task")    removed = app.delete(todo["id"])    assert removed["task"] == "Task"    assert app.list_todos() == []def test_delete_persists_to_json(app, tmp_path):    todo = app.add("Task")    app.delete(todo["id"])    data = json.loads((tmp_path / "todos.json").read_text())    assert data["todos"] == []def test_delete_missing_returns_none(app):    assert app.delete(999) is None# --- CLI ------------------------------------------------------------------def test_cli_add_and_list(tmp_path, capsys):    filepath = tmp_path / "todos.json"    assert main(["--file", str(filepath), "add", "Buy milk"]) == 0    assert "Added todo #1: Buy milk" in capsys.readouterr().out    assert main(["--file", str(filepath), "list"]) == 0    out = capsys.readouterr().out    assert "1." in out    assert "Buy milk" in outdef test_cli_list_empty(tmp_path, capsys):    filepath = tmp_path / "todos.json"    assert main(["--file", str(filepath), "list"]) == 0    assert "No todos." in capsys.readouterr().outdef test_cli_done_and_delete(tmp_path, capsys):    filepath = tmp_path / "todos.json"    main(["--file", str(filepath), "add", "Task"])    assert main(["--file", str(filepath), "done", "1"]) == 0    assert "Done: Task" in capsys.readouterr().out    assert main(["--file", str(filepath), "delete", "1"]) == 0    assert "Deleted: Task" in capsys.readouterr().outdef test_cli_done_missing_id_returns_nonzero(tmp_path, capsys):    filepath = tmp_path / "todos.json"    assert main(["--file", str(filepath), "done", "1"]) == 1    assert "not found" in capsys.readouterr().errdef test_cli_delete_missing_id_returns_nonzero(tmp_path, capsys):    filepath = tmp_path / "todos.json"    assert main(["--file", str(filepath), "delete", "1"]) == 1    assert "not found" in capsys.readouterr().errClaude Code pytest 复验输出_after_delete_does_not_reuse_id PASSED             [ 22%]test_todo.py::test_ids_are_monotonic_across_restarts PASSED              [ 27%]test_todo.py::test_list_empty_when_no_file PASSED                        [ 31%]test_todo.py::test_list_empty_when_file_is_empty PASSED                  [ 36%]test_todo.py::test_add_over_empty_file PASSED                            [ 40%]test_todo.py::test_list_empty_when_file_is_empty_store PASSED            [ 45%]test_todo.py::test_list_returns_all_todos_in_order PASSED                [ 50%]test_todo.py::test_done_marks_todo PASSED                                [ 54%]test_todo.py::test_done_persists_to_json PASSED                          [ 59%]test_todo.py::test_done_missing_returns_none PASSED                      [ 63%]test_todo.py::test_delete_removes_todo PASSED                            [ 68%]test_todo.py::test_delete_persists_to_json PASSED                        [ 72%]test_todo.py::test_delete_missing_returns_none PASSED                    [ 77%]test_todo.py::test_cli_add_and_list PASSED                               [ 81%]test_todo.py::test_cli_list_empty PASSED                                 [ 86%]test_todo.py::test_cli_done_and_delete PASSED                            [ 90%]test_todo.py::test_cli_done_missing_id_returns_nonzero PASSED            [ 95%]test_todo.py::test_cli_delete_missing_id_returns_nonzero PASSED          [100%]============================== 22 passed in 0.16s ==============================OpenCode v1.18.12 — 12/12OpenCode 生成的 todo.py (3444 bytes)#!/usr/bin/env python3"""A simple CLI todo app with JSON persistence."""import argparseimport jsonimport osimport sysDEFAULT_STORE = "todos.json"class TodoApp:    def __init__(self, store_path=DEFAULT_STORE):        self.store_path = store_path        self.todos = self._load()    def _load(self):        if not os.path.exists(self.store_path):            return []        try:            with open(self.store_path, "r") as fh:                data = json.load(fh)        except (json.JSONDecodeError, OSError):            return []        if not isinstance(data, list):            return []        return data    def _save(self):        with open(self.store_path, "w") as fh:            json.dump(self.todos, fh, indent=2)    def add(self, title):        todo = {"id": self._next_id(), "title": title, "done": False}        self.todos.append(todo)        self._save()        return todo    def _next_id(self):        return max((t["id"] for t in self.todos), default=0) + 1    def list(self, show_done=True):        todos = self.todos        if not show_done:            todos = [t for t in todos if not t["done"]]        return todos    def done(self, todo_id):        for todo in self.todos:            if todo["id"] == todo_id:                todo["done"] = True                self._save()                return todo        raise KeyError(f"No todo with id {todo_id}")    def delete(self, todo_id):        for todo in self.todos:            if todo["id"] == todo_id:                self.todos.remove(todo)                self._save()                return todo        raise KeyError(f"No todo with id {todo_id}")def main(argv=None):    parser = argparse.ArgumentParser(prog="todo", description="A simple todo app.")    parser.add_argument(        "--store", default=DEFAULT_STORE, help="path to the JSON store file"    )    sub = parser.add_subparsers(dest="command", required=True)    p_add = sub.add_parser("add", help="add a todo")    p_add.add_argument("title", help="title of the todo")    p_list = sub.add_parser("list", help="list todos")    p_list.add_argument(        "--all", action="store_true", help="show completed todos too"    )    p_done = sub.add_parser("done", help="mark a todo as done")    p_done.add_argument("id", type=int, help="todo id")    p_delete = sub.add_parser("delete", help="delete a todo")    p_delete.add_argument("id", type=int, help="todo id")    args = parser.parse_args(argv)    app = TodoApp(args.store)    try:        if args.command == "add":            todo = app.add(args.title)            print(f"Added todo {todo['id']}: {todo['title']}")        elif args.command == "list":            todos = app.list(show_done=args.all)            if not todos:                print("No todos.")                return 0            for todo in todos:                status = "[x]" if todo["done"] else "[ ]"                print(f"{status}{todo['id']}: {todo['title']}")        elif args.command == "done":            todo = app.done(args.id)            print(f"Marked todo {todo['id']} as done: {todo['title']}")        elif args.command == "delete":            todo = app.delete(args.id)            print(f"Deleted todo {todo['id']}: {todo['title']}")    except KeyError as exc:        print(str(exc), file=sys.stderr)        return 1    return 0if __name__ == "__main__":    sys.exit(main())OpenCode 生成的 test_todo.py (2052 bytes)import jsonimport osimport pytestfrom todo import TodoApp@pytest.fixturedef app(tmp_path):    store = tmp_path / "todos.json"    return TodoApp(str(store))def test_add_creates_todo(app):    todo = app.add("Buy milk")    assert todo["title"] == "Buy milk"    assert todo["done"] is False    assert todo["id"] == 1def test_add_persists_to_disk(app):    app.add("Walk the dog")    assert os.path.exists(app.store_path)    with open(app.store_path) as fh:        data = json.load(fh)    assert data[0]["title"] == "Walk the dog"def test_list_empty(app):    assert app.list() == []def test_list_returns_all_todos(app):    app.add("A")    app.add("B")    assert len(app.list()) == 2def test_list_hides_done_when_requested(app):    app.add("A")    app.add("B")    app.done(1)    remaining = app.list(show_done=False)    assert [t["id"] for t in remaining] == [2]def test_done_marks_todo_completed(app):    app.add("A")    todo = app.done(1)    assert todo["done"] is True    assert app.todos[0]["done"] is Truedef test_done_unknown_id_raises(app):    with pytest.raises(KeyError):        app.done(999)def test_delete_removes_todo(app):    app.add("A")    app.add("B")    todo = app.delete(1)    assert todo["id"] == 1    assert len(app.list()) == 1    assert app.list()[0]["id"] == 2def test_delete_unknown_id_raises(app):    with pytest.raises(KeyError):        app.delete(999)def test_next_id_increments_after_delete(app):    app.add("A")    app.add("B")    app.delete(1)    todo = app.add("C")    assert todo["id"] == 3def test_load_existing_store(tmp_path):    store = tmp_path / "todos.json"    store.write_text(json.dumps([{"id": 1, "title": "existing", "done": True}]))    app = TodoApp(str(store))    assert len(app.list()) == 1    assert app.list()[0]["title"] == "existing"def test_load_corrupt_store_returns_empty(tmp_path):    store = tmp_path / "todos.json"    store.write_text("{not valid json")    app = TodoApp(str(store))    assert app.list() == []OpenCode pytest 复验输出============================= test session starts ==============================platform linux -- Python 3.13.5, pytest-9.1.1, pluggy-1.6.0 -- /tmp/bench-venv/bin/pythoncachedir: .pytest_cacherootdir: /home/agent/coding-benchmark/test-opencodecollecting ... collected 12 itemstest_todo.py::test_add_creates_todo PASSED                               [  8%]test_todo.py::test_add_persists_to_disk PASSED                           [ 16%]test_todo.py::test_list_empty PASSED                                     [ 25%]test_todo.py::test_list_returns_all_todos PASSED                         [ 33%]test_todo.py::test_list_hides_done_when_requested PASSED                 [ 41%]test_todo.py::test_done_marks_todo_completed PASSED                      [ 50%]test_todo.py::test_done_unknown_id_raises PASSED                         [ 58%]test_todo.py::test_delete_removes_todo PASSED                            [ 66%]test_todo.py::test_delete_unknown_id_raises PASSED                       [ 75%]test_todo.py::test_next_id_increments_after_delete PASSED                [ 83%]test_todo.py::test_load_existing_store PASSED                            [ 91%]test_todo.py::test_load_corrupt_store_returns_empty PASSED               [100%]============================== 12 passed in 0.07s ==============================Codex v0.50.0 — 13/13Codex 生成的 todo.py (4025 bytes)#!/usr/bin/env python3"""A simple command-line todo application with JSON persistence."""import argparseimport jsonimport sysfrom pathlib import PathDEFAULT_FILE = Path(__file__).parent / "todos.json"def load_todos(path=DEFAULT_FILE):    """Load todos from a JSON file, returning an empty list if missing or corrupt."""    if not Path(path).exists():        return []    try:        with open(path, "r", encoding="utf-8") as f:            data = json.load(f)    except (json.JSONDecodeError, OSError):        return []    return data if isinstance(data, list) else []def save_todos(todos, path=DEFAULT_FILE):    """Save a list of todos to a JSON file."""    path = Path(path)    path.parent.mkdir(parents=True, exist_ok=True)    with open(path, "w", encoding="utf-8") as f:        json.dump(todos, f, indent=2)def add_todo(description, path=DEFAULT_FILE):    """Add a new todo and return its id."""    todos = load_todos(path)    todo_id = max((t["id"] for t in todos), default=0) + 1    todos.append({"id": todo_id, "description": description, "done": False})    save_todos(todos, path)    return todo_iddef list_todos(path=DEFAULT_FILE):    """Return the list of todos."""    return load_todos(path)def mark_done(todo_id, path=DEFAULT_FILE):    """Mark a todo as done; return True if it was found."""    todos = load_todos(path)    for todo in todos:        if todo["id"] == todo_id:            todo["done"] = True            save_todos(todos, path)            return True    return Falsedef delete_todo(todo_id, path=DEFAULT_FILE):    """Delete a todo; return True if it was found."""    todos = load_todos(path)    remaining = [t for t in todos if t["id"] != todo_id]    if len(remaining) == len(todos):        return False    save_todos(remaining, path)    return Truedef main(argv=None):    """Parse command-line arguments and run the requested command."""    parser = argparse.ArgumentParser(prog="todo", description="A simple todo app.")    subparsers = parser.add_subparsers(dest="command", required=True)    add_parser = subparsers.add_parser("add", help="Add a new todo")    add_parser.add_argument("description", nargs="+", help="Todo description")    add_parser.add_argument("-f", "--file", default=DEFAULT_FILE, help="JSON file to use")    list_parser = subparsers.add_parser("list", help="List all todos")    list_parser.add_argument("-f", "--file", default=DEFAULT_FILE, help="JSON file to use")    done_parser = subparsers.add_parser("done", help="Mark a todo as done")    done_parser.add_argument("todo_id", type=int, help="Todo ID")    done_parser.add_argument("-f", "--file", default=DEFAULT_FILE, help="JSON file to use")    delete_parser = subparsers.add_parser("delete", help="Delete a todo")    delete_parser.add_argument("todo_id", type=int, help="Todo ID")    delete_parser.add_argument("-f", "--file", default=DEFAULT_FILE, help="JSON file to use")    args = parser.parse_args(argv)    if args.command == "add":        description = " ".join(args.description)        todo_id = add_todo(description, args.file)        print(f"Added todo #{todo_id}: {description}")    elif args.command == "list":        todos = list_todos(args.file)        if not todos:            print("No todos.")            return 0        for todo in todos:            status = "[x]" if todo["done"] else "[ ]"            print(f"{status}{todo['id']}: {todo['description']}")    elif args.command == "done":        if mark_done(args.todo_id, args.file):            print(f"Marked todo #{args.todo_id} as done.")        else:            print(f"No todo with id {args.todo_id}.", file=sys.stderr)            return 1    elif args.command == "delete":        if delete_todo(args.todo_id, args.file):            print(f"Deleted todo #{args.todo_id}.")        else:            print(f"No todo with id {args.todo_id}.", file=sys.stderr)            return 1    return 0if __name__ == "__main__":    sys.exit(main())Codex 生成的 test_todo.py (3197 bytes)import pytestimport tododef test_load_missing_file_returns_empty_list(tmp_path):    assert todo.load_todos(tmp_path / "nope.json") == []def test_save_and_load_round_trip(tmp_path):    path = tmp_path / "todos.json"    todos = [{"id": 1, "description": "buy milk", "done": False}]    todo.save_todos(todos, path)    assert todo.load_todos(path) == todosdef test_load_corrupt_json_returns_empty_list(tmp_path):    path = tmp_path / "todos.json"    path.write_text("{not json", encoding="utf-8")    assert todo.load_todos(path) == []def test_load_non_list_json_returns_empty_list(tmp_path):    path = tmp_path / "todos.json"    path.write_text('{"key": "value"}', encoding="utf-8")    assert todo.load_todos(path) == []def test_add_todo_assigns_incrementing_ids(tmp_path):    path = tmp_path / "todos.json"    first = todo.add_todo("first task", path)    second = todo.add_todo("second task", path)    assert first == 1    assert second == 2    todos = todo.load_todos(path)    assert todos == [        {"id": 1, "description": "first task", "done": False},        {"id": 2, "description": "second task", "done": False},    ]def test_mark_done(tmp_path):    path = tmp_path / "todos.json"    todo.add_todo("task", path)    assert todo.mark_done(1, path) is True    assert todo.load_todos(path)[0]["done"] is Truedef test_mark_done_missing_id_returns_false(tmp_path):    path = tmp_path / "todos.json"    assert todo.mark_done(99, path) is Falsedef test_delete_todo(tmp_path):    path = tmp_path / "todos.json"    todo.add_todo("keep", path)    todo.add_todo("remove", path)    assert todo.delete_todo(2, path) is True    assert [t["description"] for t in todo.load_todos(path)] == ["keep"]def test_delete_missing_id_returns_false(tmp_path):    path = tmp_path / "todos.json"    assert todo.delete_todo(99, path) is Falsedef test_main_add_and_list(tmp_path, capsys):    path = tmp_path / "todos.json"    assert todo.main(["add", "buy", "milk", "-f", str(path)]) == 0    assert todo.main(["list", "-f", str(path)]) == 0    out = capsys.readouterr().out    assert "buy milk" in out    assert "Added todo" in outdef test_list_shows_done_status(tmp_path, capsys):    path = tmp_path / "todos.json"    todo.add_todo("pending", path)    todo.add_todo("finished", path)    todo.mark_done(2, path)    todo.main(["list", "-f", str(path)])    out = capsys.readouterr().out    assert "[ ] 1: pending" in out    assert "[x] 2: finished" in outdef test_main_done_and_delete(tmp_path, capsys):    path = tmp_path / "todos.json"    todo.main(["add", "task", "-f", str(path)])    assert todo.main(["done", "1", "-f", str(path)]) == 0    assert todo.main(["delete", "1", "-f", str(path)]) == 0    assert todo.main(["list", "-f", str(path)]) == 0    assert "No todos." in capsys.readouterr().outdef test_main_missing_todo_returns_error(tmp_path, capsys):    path = tmp_path / "todos.json"    assert todo.main(["done", "1", "-f", str(path)]) == 1    assert "No todo with id 1" in capsys.readouterr().err    assert todo.main(["delete", "1", "-f", str(path)]) == 1    assert "No todo with id 1" in capsys.readouterr().errCodex pytest 复验输出============================= test session starts ==============================platform linux -- Python 3.13.5, pytest-9.1.1, pluggy-1.6.0 -- /tmp/bench-venv/bin/pythoncachedir: .pytest_cacherootdir: /home/agent/coding-benchmark/test-codexcollecting ... collected 13 itemstest_todo.py::test_load_missing_file_returns_empty_list PASSED           [  7%]test_todo.py::test_save_and_load_round_trip PASSED                       [ 15%]test_todo.py::test_load_corrupt_json_returns_empty_list PASSED           [ 23%]test_todo.py::test_load_non_list_json_returns_empty_list PASSED          [ 30%]test_todo.py::test_add_todo_assigns_incrementing_ids PASSED              [ 38%]test_todo.py::test_mark_done PASSED                                      [ 46%]test_todo.py::test_mark_done_missing_id_returns_false PASSED             [ 53%]test_todo.py::test_delete_todo PASSED                                    [ 61%]test_todo.py::test_delete_missing_id_returns_false PASSED                [ 69%]test_todo.py::test_main_add_and_list PASSED                              [ 76%]test_todo.py::test_list_shows_done_status PASSED                         [ 84%]test_todo.py::test_main_done_and_delete PASSED                           [ 92%]test_todo.py::test_main_missing_todo_returns_error PASSED                [100%]============================== 13 passed in 0.10s ==============================Cline v3.0.49 — 24/24 (手动修复后)Cline 生成的 todo.py (4751 bytes)#!/usr/bin/env python3"""A simple command-line todo app with JSON persistence.Usage:    python todo.py add "Buy milk"    python todo.py list    python todo.py done 1    python todo.py delete 1Tasks are stored in a JSON file (todo.json by default). Set the TODO_FILEenvironment variable to use a different data file."""import argparseimport jsonimport osimport sysDEFAULT_DATA_FILE = "todo.json"class TodoList:    """A todo list backed by a JSON file.    Each task is a dict: {"id"int"description": str, "done"bool}.    """    def __init__(self, data_file=DEFAULT_DATA_FILE):        self.data_file = data_file        self.tasks = []        self._max_id = 0        self.load()    def load(self):        """Load tasks from the JSON data file.        A missing or corrupt file simply yields an empty list so the app        never crashes on startup.        """        try:            with open(self.data_file, "r", encoding="utf-8") as fh:                self.tasks = json.load(fh)        except (FileNotFoundError, json.JSONDecodeError, OSError):            self.tasks = []        self._max_id = max((task["id"] for task in self.tasks), default=0)    def save(self):        """Persist tasks to the JSON data file."""        with open(self.data_file, "w", encoding="utf-8") as fh:            json.dump(self.tasks, fh, indent=2)    def _next_id(self):        """Return the next task id: never reuse an id, even after deletion."""        self._max_id += 1        return self._max_id    def add(self, description):        """Add a new task and return it."""        task = {"id": self._next_id(), "description": description, "done": False}        self.tasks.append(task)        self.save()        return task    def get(self, task_id):        """Return the task with the given id, or None if it does not exist."""        for task in self.tasks:            if task["id"] == task_id:                return task        return None    def list_tasks(self):        """Return a copy of all tasks in insertion order."""        return list(self.tasks)    def done(self, task_id):        """Mark the task with the given id as done and return it.        Raises KeyError if no such task exists.        """        task = self.get(task_id)        if task is None:            raise KeyError(f"no task with id {task_id}")        task["done"] = True        self.save()        return task    def delete(self, task_id):        """Remove the task with the given id and return it.        Raises KeyError if no such task exists.        """        for index, task in enumerate(self.tasks):            if task["id"] == task_id:                removed = self.tasks.pop(index)                self.save()                return removed        raise KeyError(f"no task with id {task_id}")def format_task(task):    """Render a task as a one-line string, e.g. '[x] 1. Buy milk'."""    marker = "[x]" if task["done"] else "[ ]"    return f"{marker}{task['id']}. {task['description']}"def main(argv=None):    parser = argparse.ArgumentParser(        prog="todo", description="A simple CLI todo app with JSON persistence."    )    subparsers = parser.add_subparsers(dest="command", required=True)    add_parser = subparsers.add_parser("add", help="add a new task")    add_parser.add_argument("description", help="task description")    subparsers.add_parser("list", help="list all tasks")    done_parser = subparsers.add_parser("done", help="mark a task as done")    done_parser.add_argument("id", type=int, help="task id")    delete_parser = subparsers.add_parser("delete", help="delete a task")    delete_parser.add_argument("id", type=int, help="task id")    args = parser.parse_args(argv)    data_file = os.environ.get("TODO_FILE", DEFAULT_DATA_FILE)    todo = TodoList(data_file)    try:        if args.command == "add":            task = todo.add(args.description)            print(f"Added task {task['id']}: {task['description']}")        elif args.command == "list":            tasks = todo.list_tasks()            if not tasks:                print("No tasks yet.")            else:                for task in tasks:                    print(format_task(task))        elif args.command == "done":            todo.done(args.id)            print(f"Marked task {args.id} as done.")        elif args.command == "delete":            task = todo.delete(args.id)            print(f"Deleted task {task['id']}: {task['description']}")    except KeyError as exc:        msg = exc.args[0] if exc.args else str(exc)        print(f"Error: {msg}", file=sys.stderr)        return 1    return 0if __name__ == "__main__":    sys.exit(main())Cline 生成的 test_todo.py (5061 bytes)"""Unit tests for the todo CLI app."""import jsonimport pytestimport todo@pytest.fixturedef todo_list(tmp_path):    """A TodoList backed by a temp JSON file."""    return todo.TodoList(tmp_path / "tasks.json")@pytest.fixturedef cli(tmp_path, monkeypatch):    """Point the CLI at a temp data file via the TODO_FILE env var."""    data_file = tmp_path / "tasks.json"    monkeypatch.setenv("TODO_FILE", str(data_file))    return data_file# --- Model: add ---def test_add_creates_task(todo_list):    task = todo_list.add("Buy milk")    assert task == {"id": 1, "description": "Buy milk", "done": False}    assert todo_list.tasks == [task]def test_add_persists_to_disk(todo_list):    todo_list.add("Buy milk")    assert todo_list.data_file.exists()    saved = json.loads(todo_list.data_file.read_text())    assert saved == [{"id": 1, "description": "Buy milk", "done": False}]def test_add_increments_ids(todo_list):    first = todo_list.add("First")    second = todo_list.add("Second")    assert first["id"] == 1    assert second["id"] == 2# --- Model: list ---def test_list_empty(todo_list):    assert todo_list.list_tasks() == []def test_list_returns_all_tasks(todo_list):    todo_list.add("First")    todo_list.add("Second")    tasks = todo_list.list_tasks()    assert [task["description"] for task in tasks] == ["First", "Second"]def test_list_returns_copy(todo_list):    todo_list.add("First")    listing = todo_list.list_tasks()    listing.clear()    assert len(todo_list.tasks) == 1# --- Model: done ---def test_done_marks_task(todo_list):    todo_list.add("First")    task = todo_list.done(1)    assert task["done"] is True    assert todo_list.get(1)["done"] is Truedef test_done_persists(todo_list):    todo_list.add("First")    todo_list.done(1)    saved = json.loads(todo_list.data_file.read_text())    assert saved[0]["done"] is Truedef test_done_unknown_id_raises(todo_list):    with pytest.raises(KeyError):        todo_list.done(99)# --- Model: delete ---def test_delete_removes_task(todo_list):    todo_list.add("First")    todo_list.add("Second")    removed = todo_list.delete(1)    assert removed["id"] == 1    assert [t["description"] for t in todo_list.list_tasks()] == ["Second"]def test_delete_persists(todo_list):    todo_list.add("First")    todo_list.delete(1)    saved = json.loads(todo_list.data_file.read_text())    assert saved == []def test_delete_unknown_id_raises(todo_list):    with pytest.raises(KeyError):        todo_list.delete(99)def test_next_id_survives_delete(todo_list):    # Ids are derived from max existing id, so a deleted id is not reused.    todo_list.add("First")    todo_list.delete(1)    task = todo_list.add("Second")    assert task["id"] == 2# --- Model: persistence / loading ---def test_reload_from_disk(tmp_path):    path = tmp_path / "tasks.json"    todo.TodoList(path).add("Persisted")    reloaded = todo.TodoList(path)    assert reloaded.list_tasks() == [        {"id": 1, "description": "Persisted", "done": False}    ]def test_corrupt_file_starts_empty(tmp_path):    path = tmp_path / "tasks.json"    path.write_text("{not valid json")    store = todo.TodoList(path)    assert store.tasks == []def test_missing_file_starts_empty(tmp_path):    store = todo.TodoList(tmp_path / "nonexistent.json")    assert store.tasks == []def test_format_task():    assert todo.format_task({"id": 1, "description": "Buy milk", "done": False}) == (        "[ ] 1. Buy milk"    )    assert todo.format_task({"id": 2, "description": "Pay rent", "done": True}) == (        "[x] 2. Pay rent"    )# --- CLI end-to-end ---def test_cli_add(cli, capsys):    assert todo.main(["add", "Buy milk"]) == 0    assert "Added task 1: Buy milk" in capsys.readouterr().outdef test_cli_list_empty(cli, capsys):    assert todo.main(["list"]) == 0    assert "No tasks yet." in capsys.readouterr().outdef test_cli_list_shows_tasks(cli, capsys):    todo.main(["add", "First"])    todo.main(["add", "Second"])    assert todo.main(["list"]) == 0    out = capsys.readouterr().out    assert "[ ] 1. First" in out    assert "[ ] 2. Second" in outdef test_cli_done(cli, capsys):    todo.main(["add", "Task"])    assert todo.main(["done", "1"]) == 0    assert "Marked task 1 as done." in capsys.readouterr().out    todo.main(["list"])    assert "[x] 1. Task" in capsys.readouterr().outdef test_cli_delete(cli, capsys):    todo.main(["add", "Task"])    assert todo.main(["delete", "1"]) == 0    assert "Deleted task 1: Task" in capsys.readouterr().out    todo.main(["list"])    assert "No tasks yet." in capsys.readouterr().outdef test_cli_done_unknown_id_fails(cli, capsys):    assert todo.main(["done", "42"]) == 1    assert "Error: no task with id 42" in capsys.readouterr().errdef test_cli_state_persists_across_invocations(cli, capsys):    todo.main(["add", "Persist me"])    todo.main(["list"])    assert "1. Persist me" in capsys.readouterr().outCline pytest 复验输出e_marks_task PASSED                                [ 29%]test_todo.py::test_done_persists PASSED                                  [ 33%]test_todo.py::test_done_unknown_id_raises PASSED                         [ 37%]test_todo.py::test_delete_removes_task PASSED                            [ 41%]test_todo.py::test_delete_persists PASSED                                [ 45%]test_todo.py::test_delete_unknown_id_raises PASSED                       [ 50%]test_todo.py::test_next_id_survives_delete PASSED                        [ 54%]test_todo.py::test_reload_from_disk PASSED                               [ 58%]test_todo.py::test_corrupt_file_starts_empty PASSED                      [ 62%]test_todo.py::test_missing_file_starts_empty PASSED                      [ 66%]test_todo.py::test_format_task PASSED                                    [ 70%]test_todo.py::test_cli_add PASSED                                        [ 75%]test_todo.py::test_cli_list_empty PASSED                                 [ 79%]test_todo.py::test_cli_list_shows_tasks PASSED                           [ 83%]test_todo.py::test_cli_done PASSED                                       [ 87%]test_todo.py::test_cli_delete PASSED                                     [ 91%]test_todo.py::test_cli_done_unknown_id_fails PASSED                      [ 95%]test_todo.py::test_cli_state_persists_across_invocations PASSED          [100%]============================== 24 passed in 0.16s ==============================Kilo Code v7.4.17 — 10/10Kilo Code 生成的 todo.py (2985 bytes)#!/usr/bin/env python3"""A simple CLI todo app with JSON persistence."""import argparseimport jsonimport sysfrom pathlib import PathDEFAULT_STORE = Path(__file__).with_name("todos.json")class Todo:    def __init__(self, text, done=False, store_path=DEFAULT_STORE):        self.text = text        self.is_done = done        self.store_path = Path(store_path)    @property    def store(self):        if not self.store_path.exists():            return []        return json.loads(self.store_path.read_text() or "[]")    @store.setter    def store(self, items):        self.store_path.write_text(json.dumps(items, indent=2))    def _next_id(self, items):        return max((item["id"] for item in items), default=0) + 1    def add(self):        items = self.store        todo_id = self._next_id(items)        items.append({"id": todo_id, "text": self.text, "done": self.is_done})        self.store = items        return todo_id    def list(self):        return self.store    def done(self, todo_id):        items = self.store        for item in items:            if item["id"] == todo_id:                item["done"] = True                self.store = items                return True        return False    def delete(self, todo_id):        items = self.store        new_items = [item for item in items if item["id"] != todo_id]        if len(new_items) == len(items):            return False        self.store = new_items        return Truedef main(argv=None):    parser = argparse.ArgumentParser(description="Manage a todo list.")    subparsers = parser.add_subparsers(dest="command", required=True)    add_parser = subparsers.add_parser("add", help="Add a new todo.")    add_parser.add_argument("text", help="The todo text.")    subparsers.add_parser("list", help="List all todos.")    done_parser = subparsers.add_parser("done", help="Mark a todo done.")    done_parser.add_argument("id", type=int, help="The todo id.")    delete_parser = subparsers.add_parser("delete", help="Delete a todo.")    delete_parser.add_argument("id", type=int, help="The todo id.")    args = parser.parse_args(argv)    todo = Todo("")    if args.command == "add":        todo.text = args.text        todo_id = todo.add()        print(f"Added todo {todo_id}")    elif args.command == "list":        for item in todo.list():            status = "x" if item["done"] else " "            print(f"{item['id']} [{status}] {item['text']}")    elif args.command == "done":        if todo.done(args.id):            print(f"Marked todo {args.id} as done")        else:            print(f"Todo {args.id} not found", file=sys.stderr)            return 1    elif args.command == "delete":        if todo.delete(args.id):            print(f"Deleted todo {args.id}")        else:            print(f"Todo {args.id} not found", file=sys.stderr)            return 1    return 0if __name__ == "__main__":    sys.exit(main())Kilo Code 生成的 test_todo.py (1569 bytes)"""Unit tests for todo.py."""import jsonimport pytestfrom todo import Todo@pytest.fixturedef todo(tmp_path):    return Todo("", store_path=tmp_path / "todos.json")def test_add_creates_todo(todo):    todo_id = todo.add()    items = todo.list()    assert len(items) == 1    assert items[0]["id"] == todo_id    assert items[0]["text"] == ""    assert items[0]["done"] is Falsedef test_add_assigns_incrementing_ids(todo):    first = todo.add()    second = todo.add()    assert second == first + 1def test_add_persists_to_disk(todo):    todo.add()    raw = todo.store_path.read_text()    assert json.loads(raw)[0]["text"] == ""def test_list_empty(todo):    assert todo.list() == []def test_list_returns_all_todos(todo):    todo.text = "first"    todo.add()    todo.text = "second"    todo.add()    assert [item["text"] for item in todo.list()] == ["first", "second"]def test_done_marks_todo(todo):    todo_id = todo.add()    assert todo.done(todo_id) is True    items = todo.list()    assert items[0]["done"] is Truedef test_done_missing_id_returns_false(todo):    assert todo.done(999) is Falsedef test_delete_removes_todo(todo):    todo_id = todo.add()    assert todo.delete(todo_id) is True    assert todo.list() == []def test_delete_missing_id_returns_false(todo):    assert todo.delete(999) is Falsedef test_delete_only_removes_target(todo):    first = todo.add()    todo.delete(first)    assert todo.list() == []    second = todo.add()    assert [item["id"] for item in todo.list()] == [second]Kilo Code pytest 复验输出============================= test session starts ==============================platform linux -- Python 3.13.5, pytest-9.1.1, pluggy-1.6.0 -- /tmp/bench-venv/bin/pythoncachedir: .pytest_cacherootdir: /home/agent/coding-benchmark/test-kilocollecting ... collected 10 itemstest_todo.py::test_add_creates_todo PASSED                               [ 10%]test_todo.py::test_add_assigns_incrementing_ids PASSED                   [ 20%]test_todo.py::test_add_persists_to_disk PASSED                           [ 30%]test_todo.py::test_list_empty PASSED                                     [ 40%]test_todo.py::test_list_returns_all_todos PASSED                         [ 50%]test_todo.py::test_done_marks_todo PASSED                                [ 60%]test_todo.py::test_done_missing_id_returns_false PASSED                  [ 70%]test_todo.py::test_delete_removes_todo PASSED                            [ 80%]test_todo.py::test_delete_missing_id_returns_false PASSED                [ 90%]test_todo.py::test_delete_only_removes_target PASSED                     [100%]============================== 10 passed in 0.06s ==============================Crush vgo 编译版 — 13/13Crush 生成的 todo.py (3654 bytes)#!/usr/bin/env python3"""A minimal CLI todo app with JSON persistence.Commands:    add <description>   Add a new todo    list                List all todos (with done status)    done <id>           Mark a todo as done    delete <id>         Delete a todo"""import argparseimport jsonimport osimport sysDEFAULT_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "todos.json")def load_todos(path=DEFAULT_FILE):    """Load todos from the JSON file, returning an empty list if missing."""    if not os.path.exists(path):        return []    try:        with open(path, "r", encoding="utf-8") as f:            data = json.load(f)    except (json.JSONDecodeError, OSError):        return []    if not isinstance(data, list):        return []    return datadef save_todos(todos, path=DEFAULT_FILE):    """Persist todos to the JSON file."""    with open(path, "w", encoding="utf-8") as f:        json.dump(todos, f, indent=2)def next_id(todos):    """Return the next available todo id."""    return max((t.get("id", 0) for t in todos), default=0) + 1def add_todo(description, path=DEFAULT_FILE):    """Add a new todo and return it."""    todos = load_todos(path)    todo = {"id": next_id(todos), "description": description, "done": False}    todos.append(todo)    save_todos(todos, path)    return tododef list_todos(path=DEFAULT_FILE):    """Return the list of todos."""    return load_todos(path)def mark_done(todo_id, path=DEFAULT_FILE):    """Mark a todo as done. Returns the todo, or None if not found."""    todos = load_todos(path)    for todo in todos:        if todo["id"] == todo_id:            todo["done"] = True            save_todos(todos, path)            return todo    return Nonedef delete_todo(todo_id, path=DEFAULT_FILE):    """Delete a todo. Returns the deleted todo, or None if not found."""    todos = load_todos(path)    for i, todo in enumerate(todos):        if todo["id"] == todo_id:            removed = todos.pop(i)            save_todos(todos, path)            return removed    return Nonedef main(argv=None):    parser = argparse.ArgumentParser(prog="todo", description="A simple todo app")    sub = parser.add_subparsers(dest="command", required=True)    sub.add_parser("list", help="list all todos")    p_add = sub.add_parser("add", help="add a todo")    p_add.add_argument("description", help="todo description")    p_done = sub.add_parser("done", help="mark a todo as done")    p_done.add_argument("id", type=int, help="todo id")    p_del = sub.add_parser("delete", help="delete a todo")    p_del.add_argument("id", type=int, help="todo id")    args = parser.parse_args(argv)    if args.command == "add":        todo = add_todo(args.description)        print(f"Added todo #{todo['id']}: {todo['description']}")    elif args.command == "list":        todos = list_todos()        if not todos:            print("No todos.")            return 0        for todo in todos:            status = "[x]" if todo["done"] else "[ ]"            print(f"{status}{todo['id']}: {todo['description']}")    elif args.command == "done":        todo = mark_done(args.id)        if todo is None:            print(f"Todo #{args.id} not found.", file=sys.stderr)            return 1        print(f"Done: {todo['description']}")    elif args.command == "delete":        todo = delete_todo(args.id)        if todo is None:            print(f"Todo #{args.id} not found.", file=sys.stderr)            return 1        print(f"Deleted: {todo['description']}")    return 0if __name__ == "__main__":    sys.exit(main())Crush 生成的 test_todo.py (2861 bytes)"""Unit tests for todo.py."""import jsonimport osimport pytestimport todo@pytest.fixturedef todo_file(tmp_path):    """Return a path to a temp JSON file for a test."""    return str(tmp_path / "todos.json")@pytest.fixturedef populated_file(todo_file):    """Create a temp file pre-populated with two todos."""    data = [        {"id": 1, "description": "buy milk", "done": False},        {"id": 2, "description": "walk dog", "done": True},    ]    with open(todo_file, "w", encoding="utf-8") as f:        json.dump(data, f)    return todo_filedef test_load_missing_file(todo_file):    assert todo.load_todos(todo_file) == []def test_load_invalid_json(todo_file):    with open(todo_file, "w", encoding="utf-8") as f:        f.write("not json{")    assert todo.load_todos(todo_file) == []def test_load_non_list(todo_file):    with open(todo_file, "w", encoding="utf-8") as f:        json.dump({"wrong": "shape"}, f)    assert todo.load_todos(todo_file) == []def test_save_and_load_roundtrip(todo_file):    todos = [{"id": 1, "description": "x", "done": False}]    todo.save_todos(todos, todo_file)    assert todo.load_todos(todo_file) == todosdef test_next_id_empty():    assert todo.next_id([]) == 1def test_next_id_continues(populated_file):    assert todo.next_id(todo.load_todos(populated_file)) == 3def test_add_todo(todo_file):    added = todo.add_todo("buy milk", todo_file)    assert added == {"id": 1, "description": "buy milk", "done": False}    assert todo.load_todos(todo_file) == [added]def test_add_todo_increments_ids(populated_file):    first = todo.add_todo("first", populated_file)    second = todo.add_todo("second", populated_file)    assert first["id"] == 3    assert second["id"] == 4def test_list_todos(populated_file):    assert todo.list_todos(populated_file) == [        {"id": 1, "description": "buy milk", "done": False},        {"id": 2, "description": "walk dog", "done": True},    ]def test_mark_done(populated_file):    updated = todo.mark_done(1, populated_file)    assert updated == {"id": 1, "description": "buy milk", "done": True}    stored = todo.load_todos(populated_file)    assert stored[0]["done"] is True    assert stored[1]["done"] is True  # unchangeddef test_mark_done_missing(populated_file):    assert todo.mark_done(99, populated_file) is None    assert len(todo.load_todos(populated_file)) == 2def test_delete_todo(populated_file):    removed = todo.delete_todo(1, populated_file)    assert removed == {"id": 1, "description": "buy milk", "done": False}    assert todo.load_todos(populated_file) == [        {"id": 2, "description": "walk dog", "done": True}    ]def test_delete_missing(populated_file):    assert todo.delete_todo(99, populated_file) is None    assert len(todo.load_todos(populated_file)) == 2Crush pytest 复验输出============================= test session starts ==============================platform linux -- Python 3.13.5, pytest-9.1.1, pluggy-1.6.0 -- /tmp/bench-venv/bin/pythoncachedir: .pytest_cacherootdir: /home/agent/coding-benchmark/test-crushcollecting ... collected 13 itemstest_todo.py::test_load_missing_file PASSED                              [  7%]test_todo.py::test_load_invalid_json PASSED                              [ 15%]test_todo.py::test_load_non_list PASSED                                  [ 23%]test_todo.py::test_save_and_load_roundtrip PASSED                        [ 30%]test_todo.py::test_next_id_empty PASSED                                  [ 38%]test_todo.py::test_next_id_continues PASSED                              [ 46%]test_todo.py::test_add_todo PASSED                                       [ 53%]test_todo.py::test_add_todo_increments_ids PASSED                        [ 61%]test_todo.py::test_list_todos PASSED                                     [ 69%]test_todo.py::test_mark_done PASSED                                      [ 76%]test_todo.py::test_mark_done_missing PASSED                              [ 84%]test_todo.py::test_delete_todo PASSED                                    [ 92%]test_todo.py::test_delete_missing PASSED                                 [100%]============================== 13 passed in 0.07s ==============================Goose v1.45.0 — 17/17Goose 生成的 todo.py (3972 bytes)#!/usr/bin/env python3"""A simple CLI todo app with JSON persistence.Commands:    add <title>    Add a new todo    list           List all todos    done <id>      Mark a todo as done    delete <id>    Delete a todo"""import argparseimport jsonimport sysfrom pathlib import PathDEFAULT_DATA_FILE = Path(__file__).resolve().parent / "todos.json"def load_todos(path: Path = DEFAULT_DATA_FILE) -> list[dict]:    """Load todos from a JSON file. Returns an empty list if missing/invalid."""    if not path.exists():        return []    try:        with open(path, "r", encoding="utf-8") as f:            data = json.load(f)    except (json.JSONDecodeError, OSError):        return []    if not isinstance(data, list):        return []    return datadef save_todos(todos: list[dict], path: Path = DEFAULT_DATA_FILE) -> None:    """Save todos to a JSON file."""    with open(path, "w", encoding="utf-8") as f:        json.dump(todos, f, indent=2)def add_todo(title: str, todos: list[dict]) -> dict:    """Add a new todo to the list and return it."""    todo = {        "id": max((t.get("id", 0) for t in todos if isinstance(t, dict)), default=0) + 1,        "title": title,        "done": False,    }    todos.append(todo)    return tododef list_todos(todos: list[dict]) -> list[dict]:    """Return todos sorted with incomplete items first, newest first."""    return sorted(        todos,        key=lambda t: (t.get("done", False), -t.get("id", 0)),    )def mark_done(todo_id: int, todos: list[dict]) -> dict | None:    """Mark a todo as done. Returns the todo, or None if not found."""    for todo in todos:        if todo.get("id") == todo_id:            todo["done"] = True            return todo    return Nonedef delete_todo(todo_id: int, todos: list[dict]) -> dict | None:    """Delete a todo. Returns the deleted todo, or None if not found."""    for i, todo in enumerate(todos):        if todo.get("id") == todo_id:            return todos.pop(i)    return Nonedef main(argv: list[str] | None = None, data_file: Path | None = None) -> int:    """Run the CLI. Returns a process exit code (0 = success, 1 = not found)."""    parser = argparse.ArgumentParser(description="Simple CLI todo app")    subparsers = parser.add_subparsers(dest="command", required=True)    add_parser = subparsers.add_parser("add", help="Add a new todo")    add_parser.add_argument("title", help="Title of the todo")    subparsers.add_parser("list", help="List all todos")    done_parser = subparsers.add_parser("done", help="Mark a todo as done")    done_parser.add_argument("id", type=int, help="ID of the todo")    delete_parser = subparsers.add_parser("delete", help="Delete a todo")    delete_parser.add_argument("id", type=int, help="ID of the todo")    args = parser.parse_args(argv)    path = data_file or DEFAULT_DATA_FILE    todos = load_todos(path)    if args.command == "add":        todo = add_todo(args.title, todos)        save_todos(todos, path)        print(f"Added todo {todo['id']}: {todo['title']}")    elif args.command == "list":        if not todos:            print("No todos yet.")        else:            for todo in list_todos(todos):                status = "[x]" if todo.get("done") else "[ ]"                print(f"{status}{todo.get('id')}: {todo.get('title')}")    elif args.command == "done":        todo = mark_done(args.id, todos)        if todo is None:            print(f"No todo with id {args.id}")            return 1        save_todos(todos, path)        print(f"Marked todo {todo['id']} as done: {todo['title']}")    elif args.command == "delete":        todo = delete_todo(args.id, todos)        if todo is None:            print(f"No todo with id {args.id}")            return 1        save_todos(todos, path)        print(f"Deleted todo {todo['id']}: {todo['title']}")    return 0if __name__ == "__main__":    sys.exit(main())Goose 生成的 test_todo.py (4285 bytes)"""Unit tests for the todo CLI app."""import pytestfrom todo import (    add_todo,    delete_todo,    list_todos,    load_todos,    main,    mark_done,    save_todos,)# --- persistence ----------------------------------------------------------def test_load_todos_missing_file_returns_empty(tmp_path):    path = tmp_path / "todos.json"    assert load_todos(path) == []def test_save_and_load_roundtrip(tmp_path):    path = tmp_path / "todos.json"    todos = [{"id": 1, "title": "buy milk", "done": False}]    save_todos(todos, path)    assert load_todos(path) == todosdef test_load_invalid_json_returns_empty(tmp_path):    path = tmp_path / "todos.json"    path.write_text("not valid json {")    assert load_todos(path) == []def test_load_non_list_json_returns_empty(tmp_path):    path = tmp_path / "todos.json"    path.write_text('{"foo": "bar"}')    assert load_todos(path) == []# --- model operations -----------------------------------------------------def test_add_todo_assigns_incrementing_ids():    todos = []    first = add_todo("first", todos)    second = add_todo("second", todos)    assert first["id"] == 1    assert second["id"] == 2    assert first["done"] is False    assert second["title"] == "second"    assert [t["title"] for t in todos] == ["first", "second"]def test_add_todo_continues_ids_after_existing():    todos = [{"id": 5, "title": "existing", "done": True}]    new = add_todo("new", todos)    assert new["id"] == 6def test_mark_done_flags_todo():    todos = [{"id": 1, "title": "a", "done": False}]    result = mark_done(1, todos)    assert result == todos[0]    assert todos[0]["done"] is Truedef test_mark_done_missing_returns_none():    assert mark_done(99, [{"id": 1, "title": "a", "done": False}]) is Nonedef test_delete_todo_removes_it():    todos = [        {"id": 1, "title": "a", "done": False},        {"id": 2, "title": "b", "done": False},    ]    deleted = delete_todo(1, todos)    assert deleted["title"] == "a"    assert todos == [{"id": 2, "title": "b", "done": False}]def test_delete_missing_returns_none():    todos = [{"id": 1, "title": "a", "done": False}]    assert delete_todo(99, todos) is None    assert len(todos) == 1def test_list_todos_sorts_incomplete_first_newest_first():    todos = [        {"id": 1, "title": "oldest done", "done": True},        {"id": 2, "title": "older open", "done": False},        {"id": 3, "title": "newest open", "done": False},    ]    listed = list_todos(todos)    # Incomplete todos first, then newest id first within each group.    assert [t["title"] for t in listed] == ["newest open", "older open", "oldest done"]# --- CLI end-to-end -------------------------------------------------------def test_cli_add_writes_json_and_prints(tmp_path, capsys):    path = tmp_path / "todos.json"    assert main(["add", "buy milk"], data_file=path) == 0    assert load_todos(path) == [{"id": 1, "title": "buy milk", "done": False}]    assert "buy milk" in capsys.readouterr().outdef test_cli_list_empty(tmp_path, capsys):    path = tmp_path / "todos.json"    assert main(["list"], data_file=path) == 0    assert "No todos yet" in capsys.readouterr().outdef test_cli_done_and_delete_flow(tmp_path):    path = tmp_path / "todos.json"    assert main(["add", "task one"], data_file=path) == 0    assert main(["add", "task two"], data_file=path) == 0    assert main(["done", "1"], data_file=path) == 0    todos = load_todos(path)    assert todos[0]["done"] is True    assert main(["delete", "1"], data_file=path) == 0    remaining = load_todos(path)    assert [t["id"] for t in remaining] == [2]def test_cli_done_missing_returns_nonzero(tmp_path, capsys):    path = tmp_path / "todos.json"    assert main(["done", "42"], data_file=path) == 1    assert "No todo with id 42" in capsys.readouterr().outdef test_cli_delete_missing_returns_nonzero(tmp_path, capsys):    path = tmp_path / "todos.json"    assert main(["delete", "42"], data_file=path) == 1    assert "No todo with id 42" in capsys.readouterr().outdef test_cli_invalid_command_raises_system_exit(tmp_path):    path = tmp_path / "todos.json"    with pytest.raises(SystemExit):        main(["bogus"], data_file=path)Goose pytest 复验输出g-benchmark/test-goosecollecting ... collected 17 itemstest_todo.py::test_load_todos_missing_file_returns_empty PASSED          [  5%]test_todo.py::test_save_and_load_roundtrip PASSED                        [ 11%]test_todo.py::test_load_invalid_json_returns_empty PASSED                [ 17%]test_todo.py::test_load_non_list_json_returns_empty PASSED               [ 23%]test_todo.py::test_add_todo_assigns_incrementing_ids PASSED              [ 29%]test_todo.py::test_add_todo_continues_ids_after_existing PASSED          [ 35%]test_todo.py::test_mark_done_flags_todo PASSED                           [ 41%]test_todo.py::test_mark_done_missing_returns_none PASSED                 [ 47%]test_todo.py::test_delete_todo_removes_it PASSED                         [ 52%]test_todo.py::test_delete_missing_returns_none PASSED                    [ 58%]test_todo.py::test_list_todos_sorts_incomplete_first_newest_first PASSED [ 64%]test_todo.py::test_cli_add_writes_json_and_prints PASSED                 [ 70%]test_todo.py::test_cli_list_empty PASSED                                 [ 76%]test_todo.py::test_cli_done_and_delete_flow PASSED                       [ 82%]test_todo.py::test_cli_done_missing_returns_nonzero PASSED               [ 88%]test_todo.py::test_cli_delete_missing_returns_nonzero PASSED             [ 94%]test_todo.py::test_cli_invalid_command_raises_system_exit PASSED         [100%]============================== 17 passed in 0.10s ==============================
    • 测试:13/13 通过(glm_for_coding 下有个 DEFAULT_FILE 绑定 bug,换 deepseek 后自动消失)
    • 速度快(66s),token 省(14.5K input)
    • Charm 出品,UI 好看,但实用性一般
    • 测试:17/17 通过,代码质量好(126 行 todo.py 结构清晰,含损坏文件容错、时间戳、自动递增 ID)
    • 自带任务管理(todo_write 工具),agent 会自己记账
    • Linux Foundation 项目,48K stars,潜力大
    • 测试:13/13 通过,还做了完整 smoke test(add→done→list→delete 端到端)
    • 重要历史
      :glm_for_coding 下失败 4 次(heredoc 被 chr(39) 转义搞坏、代码测试不一致、连接卡死)——是模型问题不是 Codex 问题
    • 隐患
      :deepseek 是推理模型,Codex 多轮对话时触发 reasoning_content 必须回传 400 报错——复杂多轮任务会随机挂,只适合单轮/简单任务
    • Terminal-Bench 2.1 排名第一(83.4%)的工具,能力本身最强
    • Token 说明:本地 session 不记录 usage;曾尝试"账单前后差值法"但网关为共享 key(后台有 MRS refactor 等任务并发消耗),差值 0.027
    • 测试:22/22 通过
    • 亮点:自己发现并修复了 mktemp 空文件导致 json.load 崩溃的 bug(加了空文件容错 + 2 个对应测试);用 next_id 计数器保证删除后 id 不复用
    • 系统提示词最完善,工具定义最全——这也是它 token 大的原因
    • 生态最大(134K stars)、文档全、最成熟
    • 测试:12/12 通过
    • 自动发现项目 .venv 里的 pytest 9.1.1 并直接使用
    • 安装难点:GitHub 下载慢需镜像(ghfast.top),但配置极简
    • Token 数据:本地 SQLite(~/.local/share/opencode/opencode.db session 表)可精确查询
    1. 正确配置文件是 ~/.config/crush/crush.json
      (不是 config.json!改了 config.json 完全不生效)
    2. 自定义 provider 的 models 必须是数组(不是对象,否则报 model not found)
    3. crush update-providers
       会覆盖 providers.json 手动修改(远程拉取 40 个 provider)
    4. 运行 flag 是 -m provider/model(如 -m myagentlab/deepseek-v4-flash
    1. 必须用 OPENAI_API_KEY / OPENAI_BASE_URL
      (不是 GOOSE_API_KEY/GOOSE_BASE_URL!之前一直设错连默认 api.openai.com 当然超时)
    2. 流式模式会报 Stream decode error——必须在 declarative provider 里设 "supports_streaming": false
    3. 自定义 provider 要放 ~/.config/goose/custom_providers/<name>.json
基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-17 08:27:36 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/902775.html
  2. 运行时间 : 0.237697s [ 吞吐率:4.21req/s ] 内存消耗:4,932.28kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=f1c61d6f3d8e27d292268c998b2256dc
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 4.22 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.11 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001159s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001562s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000685s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000698s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001348s ]
  6. SELECT * FROM `set` [ RunTime:0.000591s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001449s ]
  8. SELECT * FROM `article` WHERE `id` = 902775 LIMIT 1 [ RunTime:0.004657s ]
  9. UPDATE `article` SET `lasttime` = 1786926456 WHERE `id` = 902775 [ RunTime:0.011736s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000816s ]
  11. SELECT * FROM `article` WHERE `id` < 902775 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001260s ]
  12. SELECT * FROM `article` WHERE `id` > 902775 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001104s ]
  13. SELECT * FROM `article` WHERE `id` < 902775 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.003399s ]
  14. SELECT * FROM `article` WHERE `id` < 902775 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.002555s ]
  15. SELECT * FROM `article` WHERE `id` < 902775 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.005130s ]
0.241392s