refactor(coding-agent): renamed todo_write tool to todo

- Renamed `TodoWriteTool` to `TodoTool` and its source/prompt files.
- Updated tool registration, schema, renderers, and gating to `todo`.
- Adjusted cursor provider native tool names and tests to match.
- Renamed strike-animation constants and `todo-error-reminder` type.
This commit is contained in:
can1357
2026-06-04 02:45:30 +02:00
parent 67ed82f024
commit dc4aeb7b88
51 changed files with 227 additions and 225 deletions
@@ -203,7 +203,7 @@ import type { CheckpointState } from "../tools/checkpoint";
import { outputMeta } from "../tools/output-meta";
import { normalizeLocalScheme, resolveToCwd } from "../tools/path-utils";
import { isAutoQaEnabled } from "../tools/report-tool-issue";
import { getLatestTodoPhasesFromEntries, type TodoItem, type TodoPhase } from "../tools/todo-write";
import { getLatestTodoPhasesFromEntries, type TodoItem, type TodoPhase } from "../tools/todo";
import { ToolAbortError, ToolError } from "../tools/tool-errors";
import { clampTimeout } from "../tools/tool-timeouts";
import { parseCommandArgs } from "../utils/command-args";
@@ -1807,21 +1807,21 @@ export class AgentSession {
if (toolName === "edit" && details?.path) {
this.#invalidateFileCacheForPath(details.path);
}
if (toolName === "todo_write" && !isError && Array.isArray(details?.phases)) {
if (toolName === "todo" && !isError && Array.isArray(details?.phases)) {
this.setTodoPhases(details.phases);
}
if (toolName === "todo_write" && isError) {
if (toolName === "todo" && isError) {
const errorText = content?.find(part => part.type === "text")?.text;
const reminderText = [
"<system-reminder>",
"todo_write failed, so todo progress is not visible to the user.",
errorText ? `Failure: ${errorText}` : "Failure: todo_write returned an error.",
"Fix the todo payload and call todo_write again before continuing.",
"todo failed, so todo progress is not visible to the user.",
errorText ? `Failure: ${errorText}` : "Failure: todo returned an error.",
"Fix the todo payload and call todo again before continuing.",
"</system-reminder>",
].join("\n");
await this.sendCustomMessage(
{
customType: "todo-write-error-reminder",
customType: "todo-error-reminder",
content: reminderText,
display: false,
details: { toolName, errorText },
@@ -4994,7 +4994,7 @@ export class AgentSession {
// splice mutated canonical `#todoPhases` between tool calls, so the model
// observed phase totals shrinking ("5 → 4") after marking tasks done. The
// `tasks.todoClearDelay` setting is now inert; completed tasks survive
// until the next explicit `todo_write` call removes them via `rm`/`drop`.
// until the next explicit `todo` call removes them via `rm`/`drop`.
/**
* Abort current operation and wait for agent to become idle.
@@ -6553,16 +6553,16 @@ export class AgentSession {
return undefined;
}
if (!this.#toolRegistry.has("todo_write")) {
logger.warn("Eager todo enforcement skipped because todo_write is unavailable", {
if (!this.#toolRegistry.has("todo")) {
logger.warn("Eager todo enforcement skipped because todo is unavailable", {
activeToolNames: this.agent.state.tools.map(tool => tool.name),
});
return undefined;
}
const todoWriteToolChoice = buildNamedToolChoice("todo_write", this.model);
if (!todoWriteToolChoice) {
logger.warn("Eager todo enforcement skipped because the current model does not support forcing todo_write", {
const todoToolChoice = buildNamedToolChoice("todo", this.model);
if (!todoToolChoice) {
logger.warn("Eager todo enforcement skipped because the current model does not support forcing todo", {
modelApi: this.model?.api,
modelId: this.model?.id,
});
@@ -6580,7 +6580,7 @@ export class AgentSession {
attribution: "agent",
timestamp: Date.now(),
},
toolChoice: todoWriteToolChoice,
toolChoice: todoToolChoice,
};
}
/**
@@ -1,5 +1,9 @@
import { logger, toError } from "@oh-my-pi/pi-utils";
import { IndexedSessionStorage, type SessionStorageBackend, type SessionStorageIndexEntry } from "./indexed-session-storage";
import {
IndexedSessionStorage,
type SessionStorageBackend,
type SessionStorageIndexEntry,
} from "./indexed-session-storage";
/**
* Minimal subset of the `bun:redis` `RedisClient` surface used by
@@ -151,7 +151,6 @@ export class FileSessionStorage implements SessionStorage {
fs.writeFileSync(fpath, content);
}
statSync(path: string): SessionStorageStat {
const stats = fs.statSync(path);
return { size: stats.size, mtimeMs: stats.mtimeMs, mtime: stats.mtime };
@@ -465,7 +464,6 @@ export class MemorySessionStorage implements SessionStorage {
entry.mtimeMs = mtimeMs;
}
statSync(path: string): SessionStorageStat {
const entry = this.#requireEntry(path);
return {
@@ -1,4 +1,8 @@
import { IndexedSessionStorage, type SessionStorageBackend, type SessionStorageIndexEntry } from "./indexed-session-storage";
import {
IndexedSessionStorage,
type SessionStorageBackend,
type SessionStorageIndexEntry,
} from "./indexed-session-storage";
/**
* Supported `bun:sql` adapter dialects. `Bun.SQL` reports this string on
@@ -276,7 +280,9 @@ class SqlSessionStorageBackend implements SessionStorageBackend {
async readSlices(path: string, prefixBytes: number, suffixBytes: number): Promise<[string, string]> {
const values =
this.#adapter === "postgres" ? [prefixBytes, suffixBytes, path] : [prefixBytes, suffixBytes, suffixBytes, path];
this.#adapter === "postgres"
? [prefixBytes, suffixBytes, path]
: [prefixBytes, suffixBytes, suffixBytes, path];
const rows = (await this.#client.unsafe(this.#q.readSlices, values)) as SliceRow[];
const row = rows[0];
if (!row) throw enoent(path);