@zhushanwen/pi-todo 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/__tests__/todo.test.ts +74 -2
- package/src/handlers.ts +1 -1
- package/src/index.ts +3 -0
- package/src/model.ts +29 -13
- package/src/render.ts +12 -9
- package/src/tool.ts +36 -22
package/package.json
CHANGED
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
VALID_STATUSES,
|
|
12
12
|
} from "../model";
|
|
13
13
|
import { renderWidgetLines } from "../render";
|
|
14
|
+
import { createTodoSessionState } from "../state";
|
|
15
|
+
import { handleSingleUpdate } from "../tool";
|
|
14
16
|
|
|
15
17
|
// ── 数据模型 + 向后兼容 ──────────────────────────────
|
|
16
18
|
|
|
@@ -24,8 +26,8 @@ describe("Todo data model", () => {
|
|
|
24
26
|
expect(migrated.id).toBe(1);
|
|
25
27
|
});
|
|
26
28
|
|
|
27
|
-
it("should include exactly
|
|
28
|
-
expect(VALID_STATUSES).toEqual(["pending", "in_progress", "completed"]);
|
|
29
|
+
it("should include exactly four valid statuses", () => {
|
|
30
|
+
expect(VALID_STATUSES).toEqual(["pending", "in_progress", "completed", "cancelled"]);
|
|
29
31
|
});
|
|
30
32
|
|
|
31
33
|
it("should migrate verifying → in_progress", () => {
|
|
@@ -51,6 +53,18 @@ describe("Todo data model", () => {
|
|
|
51
53
|
const migrated = migrateTodo(veryOldTodo);
|
|
52
54
|
expect(migrated.status).toBe("pending");
|
|
53
55
|
});
|
|
56
|
+
|
|
57
|
+
it("should preserve isVerification flag (FR-6)", () => {
|
|
58
|
+
const todo = { id: 1, text: "run tests", status: "pending", isVerification: true } as unknown as Todo;
|
|
59
|
+
const migrated = migrateTodo(todo);
|
|
60
|
+
expect(migrated.isVerification).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should preserve cancelled status (FR-1 four-state)", () => {
|
|
64
|
+
const todo = { id: 1, text: "dropped", status: "cancelled" } as unknown as Todo;
|
|
65
|
+
const migrated = migrateTodo(todo);
|
|
66
|
+
expect(migrated.status).toBe("cancelled");
|
|
67
|
+
});
|
|
54
68
|
});
|
|
55
69
|
|
|
56
70
|
// ── todo add ────────────────────────────────────────
|
|
@@ -90,6 +104,25 @@ describe("todo add", () => {
|
|
|
90
104
|
expect(result.error).toBeUndefined();
|
|
91
105
|
expect(result.newTodos[0].text).toBe("new task");
|
|
92
106
|
});
|
|
107
|
+
|
|
108
|
+
it("should mark todos as verification when isVerification=true (FR-6)", () => {
|
|
109
|
+
const result = addTodos([], 1, ["run tests", "typecheck"], true);
|
|
110
|
+
expect(result.error).toBeUndefined();
|
|
111
|
+
expect(result.newTodos[0].isVerification).toBe(true);
|
|
112
|
+
expect(result.newTodos[1].isVerification).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should not set isVerification when omitted", () => {
|
|
116
|
+
const result = addTodos([], 1, ["regular task"]);
|
|
117
|
+
expect(result.error).toBeUndefined();
|
|
118
|
+
expect(result.newTodos[0].isVerification).toBeUndefined();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should not set isVerification when isVerification=false", () => {
|
|
122
|
+
const result = addTodos([], 1, ["regular task"], false);
|
|
123
|
+
expect(result.error).toBeUndefined();
|
|
124
|
+
expect(result.newTodos[0].isVerification).toBeUndefined();
|
|
125
|
+
});
|
|
93
126
|
});
|
|
94
127
|
|
|
95
128
|
// ── todo update batch ───────────────────────────────
|
|
@@ -144,6 +177,40 @@ describe("todo update batch", () => {
|
|
|
144
177
|
const result = updateTodos(todos, [{ id: 1, status: "banana" }]);
|
|
145
178
|
expect(result.error).toContain("invalid status");
|
|
146
179
|
});
|
|
180
|
+
|
|
181
|
+
it("FR-6: cancelled todo 不可恢复(status 更新拒绝)", () => {
|
|
182
|
+
const todos: Todo[] = [{ id: 1, text: "dropped", status: "cancelled" }];
|
|
183
|
+
const result = updateTodos(todos, [{ id: 1, status: "pending" }]);
|
|
184
|
+
expect(result.error).toBe("id 1 is cancelled");
|
|
185
|
+
expect(result.resultText).toContain("cannot be restored");
|
|
186
|
+
expect(result.updatedTodos).toEqual(todos);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("FR-6: 验证任务不可 cancelled", () => {
|
|
190
|
+
const todos: Todo[] = [{ id: 2, text: "run tests", status: "in_progress", isVerification: true }];
|
|
191
|
+
const result = updateTodos(todos, [{ id: 2, status: "cancelled" }]);
|
|
192
|
+
expect(result.error).toBe("id 2 is verification todo");
|
|
193
|
+
expect(result.resultText).toContain("cannot be cancelled");
|
|
194
|
+
expect(result.updatedTodos).toEqual(todos);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// ── handleSingleUpdate FR-6 守卫(tool 单条路径)────
|
|
199
|
+
|
|
200
|
+
describe("handleSingleUpdate FR-6 guards (tool single path)", () => {
|
|
201
|
+
it("FR-6: cancelled todo + status → cannot restore", () => {
|
|
202
|
+
const state = createTodoSessionState();
|
|
203
|
+
state.todos = [{ id: 1, text: "dropped", status: "cancelled" }];
|
|
204
|
+
const result = handleSingleUpdate(state, { action: "update", id: 1, status: "pending" });
|
|
205
|
+
expect(result.error).toBe("#1 is cancelled (cannot restore)");
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("FR-6: verification todo + status=cancelled → cannot cancel", () => {
|
|
209
|
+
const state = createTodoSessionState();
|
|
210
|
+
state.todos = [{ id: 2, text: "run tests", status: "in_progress", isVerification: true }];
|
|
211
|
+
const result = handleSingleUpdate(state, { action: "update", id: 2, status: "cancelled" });
|
|
212
|
+
expect(result.error).toBe("#2 is verification todo (cannot cancel)");
|
|
213
|
+
});
|
|
147
214
|
});
|
|
148
215
|
|
|
149
216
|
// ── completed 无拦截 ────────────────────────────────
|
|
@@ -195,6 +262,11 @@ describe("formatTodoLine", () => {
|
|
|
195
262
|
const todo: Todo = { id: 3, text: "task C", status: "completed" };
|
|
196
263
|
expect(formatTodoLine(todo)).toBe("[x] #3: task C");
|
|
197
264
|
});
|
|
265
|
+
|
|
266
|
+
it("should format cancelled todo", () => {
|
|
267
|
+
const todo: Todo = { id: 4, text: "task D", status: "cancelled" };
|
|
268
|
+
expect(formatTodoLine(todo)).toBe("[-] #4: task D");
|
|
269
|
+
});
|
|
198
270
|
});
|
|
199
271
|
|
|
200
272
|
// ── buildRender ─────────────────────────────────────
|
package/src/handlers.ts
CHANGED
|
@@ -55,7 +55,7 @@ function buildBeforeAgentStartMessage(state: TodoSessionState): { message: { cus
|
|
|
55
55
|
|
|
56
56
|
// ── 状态重建 ────────────────────────────────────────
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
function reconstructState(state: TodoSessionState, ctx: ExtensionContext): void {
|
|
59
59
|
state.todos = [];
|
|
60
60
|
state.nextId = 1;
|
|
61
61
|
state.userMessageCount = 0;
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
35
35
|
// ── 闭包内状态(session 隔离) ─────────────────────
|
|
36
36
|
const state = createTodoSessionState();
|
|
37
37
|
|
|
38
|
+
// 全解耦:不再暴露 pi.__todoGetList 跨扩展 API(goal 不再读 todo 状态)。
|
|
39
|
+
// todo 进度由 AI 自行管理,goal 不做强制检查。
|
|
40
|
+
|
|
38
41
|
// ── 刷新显示(依赖闭包 state) ─────────────────────
|
|
39
42
|
function refreshDisplay(ctx: ExtensionContext): void {
|
|
40
43
|
const statusText = renderStatusText(state.todos, ctx.ui.theme);
|
package/src/model.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Todo 数据模型 — 纯函数,不依赖 Pi 运行时。
|
|
3
|
-
*
|
|
3
|
+
* 四态: pending → in_progress → completed;任一状态 → cancelled
|
|
4
|
+
* (cancelled 不可恢复;isVerification 标记验证任务,FR-6 completion audit 用)
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
// ── 数据模型 ─────────────────────────────────────────
|
|
@@ -8,7 +9,9 @@
|
|
|
8
9
|
export interface Todo {
|
|
9
10
|
id: number;
|
|
10
11
|
text: string;
|
|
11
|
-
status: "pending" | "in_progress" | "completed";
|
|
12
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
13
|
+
/** 验证任务标记(FR-6 completion audit)。验证任务必须 completed,不可 cancelled。 */
|
|
14
|
+
isVerification?: boolean;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
export interface TodoDetails {
|
|
@@ -26,21 +29,12 @@ export interface TodoDetails {
|
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
export const VALID_STATUSES = ["pending", "in_progress", "completed"] as const;
|
|
32
|
+
export const VALID_STATUSES = ["pending", "in_progress", "completed", "cancelled"] as const;
|
|
30
33
|
|
|
31
34
|
export type ValidStatus = (typeof VALID_STATUSES)[number];
|
|
32
35
|
|
|
33
36
|
// ── 迁移/兼容 ───────────────────────────────────────
|
|
34
37
|
|
|
35
|
-
const STALE_CONTEXT_PATTERNS = ["aborted", "context canceled", "stale context", "stalecontext", "extension context no longer active"];
|
|
36
|
-
|
|
37
|
-
/** 检查错误是否表示 stale / canceled context */
|
|
38
|
-
export function isStaleContextError(error: Error | unknown): boolean {
|
|
39
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
40
|
-
const lower = msg.toLowerCase();
|
|
41
|
-
return STALE_CONTEXT_PATTERNS.some((p) => lower.includes(p));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
38
|
/** 旧格式迁移:verifying → in_progress,failed → pending,done:boolean → status */
|
|
45
39
|
export function migrateTodo(raw: Todo): Todo {
|
|
46
40
|
const record = raw as unknown as Record<string, unknown>;
|
|
@@ -66,6 +60,8 @@ export function migrateTodo(raw: Todo): Todo {
|
|
|
66
60
|
id: record.id as number,
|
|
67
61
|
text: record.text as string,
|
|
68
62
|
status,
|
|
63
|
+
// FR-6: 保留 isVerification 标记(可选字段,旧数据可能缺失)
|
|
64
|
+
isVerification: record.isVerification === true ? true : undefined,
|
|
69
65
|
};
|
|
70
66
|
}
|
|
71
67
|
|
|
@@ -101,6 +97,7 @@ export function addTodos(
|
|
|
101
97
|
currentTodos: Todo[],
|
|
102
98
|
currentNextId: number,
|
|
103
99
|
texts: string[],
|
|
100
|
+
isVerification?: boolean,
|
|
104
101
|
): AddResult {
|
|
105
102
|
if (!texts || texts.length === 0) {
|
|
106
103
|
return {
|
|
@@ -129,6 +126,8 @@ export function addTodos(
|
|
|
129
126
|
id: nextId++,
|
|
130
127
|
text: trimmed[i],
|
|
131
128
|
status: "pending" as const,
|
|
129
|
+
// FR-6: isVerification 标记验证任务(可选,仅 add 时可设)
|
|
130
|
+
isVerification: isVerification === true ? true : undefined,
|
|
132
131
|
});
|
|
133
132
|
}
|
|
134
133
|
const endId = nextId - 1;
|
|
@@ -183,6 +182,21 @@ export function updateTodos(
|
|
|
183
182
|
resultText: `Error: invalid status '${u.status}' for update item id ${u.id}`,
|
|
184
183
|
};
|
|
185
184
|
}
|
|
185
|
+
// FR-6 不变量守卫:(a) cancelled 不可恢复;(b) 验证任务不可 cancelled
|
|
186
|
+
if (todo.status === "cancelled" && u.status !== undefined) {
|
|
187
|
+
return {
|
|
188
|
+
updatedTodos: currentTodos,
|
|
189
|
+
error: `id ${u.id} is cancelled`,
|
|
190
|
+
resultText: `Error: Todo #${u.id} is cancelled and cannot be restored`,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (todo.isVerification && u.status === "cancelled") {
|
|
194
|
+
return {
|
|
195
|
+
updatedTodos: currentTodos,
|
|
196
|
+
error: `id ${u.id} is verification todo`,
|
|
197
|
+
resultText: `Error: Todo #${u.id} is a verification todo and cannot be cancelled`,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
186
200
|
}
|
|
187
201
|
|
|
188
202
|
const updated = currentTodos.map((t) => {
|
|
@@ -207,6 +221,8 @@ export function formatTodoLine(t: Todo): string {
|
|
|
207
221
|
? "x"
|
|
208
222
|
: t.status === "in_progress"
|
|
209
223
|
? "~"
|
|
210
|
-
:
|
|
224
|
+
: t.status === "cancelled"
|
|
225
|
+
? "-"
|
|
226
|
+
: " ";
|
|
211
227
|
return `[${mark}] #${t.id}: ${t.text}`;
|
|
212
228
|
}
|
package/src/render.ts
CHANGED
|
@@ -6,7 +6,6 @@ import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
|
6
6
|
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
-
buildRender,
|
|
10
9
|
getDisplayStatus,
|
|
11
10
|
type Todo,
|
|
12
11
|
type TodoDetails,
|
|
@@ -55,22 +54,25 @@ export function renderStatusText(todoList: Todo[], th: Theme): string {
|
|
|
55
54
|
// ── Widget 双列渲染 ──────────────────────────────────
|
|
56
55
|
|
|
57
56
|
/** 渲染单条 todo 的 widget 行(不含缩进),供 component.ts 复用 */
|
|
58
|
-
|
|
57
|
+
function renderWidgetItem(t: Todo, th: Theme): string {
|
|
59
58
|
const mark =
|
|
60
59
|
t.status === "completed"
|
|
61
60
|
? th.fg("success", "\u2713")
|
|
62
61
|
: t.status === "in_progress"
|
|
63
62
|
? th.fg("warning", "\u25cf")
|
|
64
|
-
:
|
|
63
|
+
: t.status === "cancelled"
|
|
64
|
+
? th.fg("error", "\u2715")
|
|
65
|
+
: th.fg("dim", "\u25cb");
|
|
65
66
|
const id = th.fg("accent", `#${t.id}`);
|
|
66
|
-
const text =
|
|
67
|
+
const text =
|
|
68
|
+
t.status === "completed" || t.status === "cancelled" ? th.fg("dim", t.text) : th.fg("text", t.text);
|
|
67
69
|
return `${mark} ${id} ${text}`;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
/** 单列布局渲染(widget 少量任务时使用) */
|
|
71
73
|
const PI_TEXT_PADDING = 2;
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
function renderSingleColumn(
|
|
74
76
|
todos: Todo[],
|
|
75
77
|
th: Theme,
|
|
76
78
|
termWidth: number,
|
|
@@ -136,7 +138,7 @@ export function renderWidgetLines(
|
|
|
136
138
|
|
|
137
139
|
// ── 列表渲染辅助函数 ─────────────────────────────────
|
|
138
140
|
|
|
139
|
-
|
|
141
|
+
function buildTodoListText(todoList: Todo[], options: { expanded: boolean }, theme: Theme): string {
|
|
140
142
|
if (todoList.length === 0) {
|
|
141
143
|
return theme.fg("dim", "No todos");
|
|
142
144
|
}
|
|
@@ -149,9 +151,11 @@ export function buildTodoListText(todoList: Todo[], options: { expanded: boolean
|
|
|
149
151
|
? theme.fg("success", "\u2713")
|
|
150
152
|
: status === "in_progress"
|
|
151
153
|
? theme.fg("warning", "\u25cf")
|
|
152
|
-
:
|
|
154
|
+
: status === "cancelled"
|
|
155
|
+
? theme.fg("error", "\u2715")
|
|
156
|
+
: theme.fg("dim", "\u25cb");
|
|
153
157
|
const itemText =
|
|
154
|
-
status === "completed" ? theme.fg("dim", t.text) : theme.fg("muted", t.text);
|
|
158
|
+
status === "completed" || status === "cancelled" ? theme.fg("dim", t.text) : theme.fg("muted", t.text);
|
|
155
159
|
listText += `\n${mark} ${theme.fg("accent", `#${t.id}`)} ${itemText}`;
|
|
156
160
|
}
|
|
157
161
|
if (!options.expanded && todoList.length > MAX_COLLAPSED_ITEMS) {
|
|
@@ -205,4 +209,3 @@ export function renderTodoResult(result: unknown, options: { expanded: boolean }
|
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
export { buildRender };
|
package/src/tool.ts
CHANGED
|
@@ -28,33 +28,39 @@ export interface TodoActionParams {
|
|
|
28
28
|
texts?: string[];
|
|
29
29
|
ids?: number[];
|
|
30
30
|
status?: string;
|
|
31
|
+
isVerification?: boolean;
|
|
31
32
|
updates?: Array<{ id: number; status?: string; text?: string }>;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
// ── TodoParams schema ────────────────────────────────
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
const TodoParams = Type.Object({
|
|
37
38
|
action: StringEnum(["list", "add", "update", "delete", "clear"] as const),
|
|
38
39
|
text: Type.Optional(Type.String({ description: "Todo text (for update action)" })),
|
|
39
40
|
id: Type.Optional(Type.Number({ description: "Todo ID (for update action)" })),
|
|
40
41
|
texts: Type.Optional(Type.Array(Type.String(), { description: "Todo text list (for add action)" })),
|
|
41
42
|
ids: Type.Optional(Type.Array(Type.Number(), { description: "Todo ID list (for delete action)" })),
|
|
42
43
|
status: Type.Optional(
|
|
43
|
-
|
|
44
|
+
StringEnum(VALID_STATUSES, { description: "Target status (for update action)" }),
|
|
45
|
+
),
|
|
46
|
+
isVerification: Type.Optional(
|
|
47
|
+
Type.Boolean({
|
|
48
|
+
description: "Mark added todos as verification tasks (for add action). Verification todos must be completed (not cancelled) before goal completion.",
|
|
49
|
+
}),
|
|
44
50
|
),
|
|
45
51
|
updates: Type.Optional(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
Type.Array(
|
|
53
|
+
Type.Object({
|
|
54
|
+
id: Type.Number({ description: "Todo ID to update" }),
|
|
55
|
+
status: Type.Optional(
|
|
56
|
+
Type.String({ description: "Target status; one of pending/in_progress/completed/cancelled" }),
|
|
57
|
+
),
|
|
58
|
+
text: Type.Optional(Type.String({ description: "New todo text" })),
|
|
59
|
+
}),
|
|
60
|
+
{ description: "Batch updates array (takes priority over single id/status/text)" },
|
|
61
|
+
),
|
|
55
62
|
),
|
|
56
|
-
)
|
|
57
|
-
});
|
|
63
|
+
});
|
|
58
64
|
|
|
59
65
|
// ── 错误结果构造 helper ──────────────────────────────
|
|
60
66
|
|
|
@@ -97,7 +103,7 @@ function handleAdd(
|
|
|
97
103
|
return { resultText: "", error: "texts required" };
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
const addResult = addTodos(state.todos, state.nextId, params.texts);
|
|
106
|
+
const addResult = addTodos(state.todos, state.nextId, params.texts, params.isVerification);
|
|
101
107
|
if (addResult.error) {
|
|
102
108
|
return { resultText: addResult.resultText || "", error: addResult.error };
|
|
103
109
|
}
|
|
@@ -134,7 +140,7 @@ function handleBatchUpdate(
|
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
/** update action: single */
|
|
137
|
-
function handleSingleUpdate(
|
|
143
|
+
export function handleSingleUpdate(
|
|
138
144
|
state: TodoSessionState,
|
|
139
145
|
params: TodoActionParams,
|
|
140
146
|
): { resultText: string; error?: string } {
|
|
@@ -151,6 +157,14 @@ function handleSingleUpdate(
|
|
|
151
157
|
const todo = state.todos.find((t) => t.id === params.id);
|
|
152
158
|
if (!todo) return { resultText: "", error: `#${params.id} not found` };
|
|
153
159
|
|
|
160
|
+
// FR-6 不变量守卫:(a) cancelled 不可恢复;(b) 验证任务不可 cancelled
|
|
161
|
+
if (todo.status === "cancelled" && params.status !== undefined) {
|
|
162
|
+
return { resultText: "", error: `#${params.id} is cancelled (cannot restore)` };
|
|
163
|
+
}
|
|
164
|
+
if (todo.isVerification && params.status === "cancelled") {
|
|
165
|
+
return { resultText: "", error: `#${params.id} is verification todo (cannot cancel)` };
|
|
166
|
+
}
|
|
167
|
+
|
|
154
168
|
if (params.status !== undefined) {
|
|
155
169
|
todo.status = params.status as Todo["status"];
|
|
156
170
|
}
|
|
@@ -219,7 +233,7 @@ function handleClear(state: TodoSessionState): string {
|
|
|
219
233
|
|
|
220
234
|
// ── Dispatcher ───────────────────────────────────────
|
|
221
235
|
|
|
222
|
-
|
|
236
|
+
function executeTodoAction(
|
|
223
237
|
params: TodoActionParams,
|
|
224
238
|
state: TodoSessionState,
|
|
225
239
|
ctx: ExtensionContext,
|
|
@@ -333,19 +347,19 @@ export function registerTodoTool(
|
|
|
333
347
|
"Manage a todo list." +
|
|
334
348
|
"\n\nAvailable actions:" +
|
|
335
349
|
"\n- list: View all todos" +
|
|
336
|
-
"\n- add: Batch add todos (requires texts array)" +
|
|
350
|
+
"\n- add: Batch add todos (requires texts array; optional isVerification marks verification tasks)" +
|
|
337
351
|
"\n- update: Update a todo (requires id, optional status/text)" +
|
|
338
352
|
"\n- delete: Batch delete todos (requires ids array)" +
|
|
339
|
-
"\n- clear: Clear all todos and reset IDs"
|
|
340
|
-
|
|
341
|
-
promptSnippet: "Use todo when breaking multi-step work into trackable items during normal (non-goal) conversation. Not for single-step operations.",
|
|
353
|
+
"\n- clear: Clear all todos and reset IDs",
|
|
354
|
+
promptSnippet: "Use todo when breaking multi-step work into trackable items. Add verification todos (isVerification=true) for checks like running tests.",
|
|
342
355
|
promptGuidelines: [
|
|
343
356
|
"[Usage] 多步骤工作(3+步)时使用。AI 自发创建,无需用户触发",
|
|
344
|
-
"[
|
|
357
|
+
"[验证任务] 执行任务 + 验证任务(isVerification=true,如 run tests / typecheck)一起建",
|
|
345
358
|
"[批量优先] 完成多项任务时使用 updates[] 批量更新,减少工具调用次数",
|
|
346
359
|
"[自动闭合] 全部完成后工具自动清理,无需手动 clear",
|
|
347
|
-
"[Not for]
|
|
360
|
+
"[Not for] 单步操作、简单对话",
|
|
348
361
|
],
|
|
362
|
+
executionMode: "sequential",
|
|
349
363
|
parameters: TodoParams,
|
|
350
364
|
|
|
351
365
|
async execute(_toolCallId: string, params: Static<typeof TodoParams>, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: ExtensionContext) {
|