@nguyenquangthai/pi-todo 0.3.5 → 0.4.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/CHANGELOG.md +24 -0
- package/package.json +1 -1
- package/src/index.ts +9 -5
- package/src/prompt-intent.ts +20 -17
- package/src/prompt.ts +79 -86
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0 (2026-07-20)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Prompt policy over-injection**: removed forced "FIRST tool call must be
|
|
8
|
+
todo_write" language from system prompt and tool description. The model is
|
|
9
|
+
no longer required to call todo_write for explain/review/audit/debug/setup
|
|
10
|
+
unless the request is genuinely multi-step.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Heuristic thresholds tightened**: increased minimum lengths for
|
|
15
|
+
verb-based, help-ask, sequenced-clause, substantive-length, and
|
|
16
|
+
multi-sentence classification to reduce false-positive cold-start nudges.
|
|
17
|
+
- **LIST_MARKERS now requires ≥2 items**: a single bullet or numbered item
|
|
18
|
+
no longer triggers multi-step classification.
|
|
19
|
+
- **EXPLICIT_TODO now requires ≥24 characters**: a short "todo" mention
|
|
20
|
+
without planning context is no longer classified as multi-step.
|
|
21
|
+
- **Cold-start language softened**: "Call todo_write NOW" → "consider creating
|
|
22
|
+
a todo list". Reminder no longer says "Do not answer without a todo list first".
|
|
23
|
+
- **Conditional system-prompt injection**: `TASK_MANAGEMENT_SECTION` is only
|
|
24
|
+
injected when the prompt is not trivial, reducing token overhead on simple
|
|
25
|
+
queries.
|
|
26
|
+
|
|
3
27
|
## 0.3.5 (2026-07-17)
|
|
4
28
|
|
|
5
29
|
### Fixed
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -15,8 +15,7 @@ import { TodoOverlay } from "./overlay.js";
|
|
|
15
15
|
import {
|
|
16
16
|
buildColdStartReminder,
|
|
17
17
|
buildCompletionUpdateReminder,
|
|
18
|
-
|
|
19
|
-
shouldNudgeCompletionUpdate,
|
|
18
|
+
classifyPrompt,
|
|
20
19
|
} from "./prompt-intent.js";
|
|
21
20
|
import { COLD_START_BOOST, TASK_MANAGEMENT_SECTION } from "./prompt.js";
|
|
22
21
|
import {
|
|
@@ -98,12 +97,17 @@ export default function (pi: ExtensionAPI): void {
|
|
|
98
97
|
pi.on("before_agent_start", async (event) => {
|
|
99
98
|
const prompt = typeof event.prompt === "string" ? event.prompt : "";
|
|
100
99
|
const open = hasOpenTodos(getTodos());
|
|
101
|
-
let systemPrompt =
|
|
100
|
+
let systemPrompt = event.systemPrompt;
|
|
101
|
+
const kind = classifyPrompt(prompt).kind;
|
|
102
|
+
// Only inject task-management instructions when the prompt is not trivial.
|
|
103
|
+
if (kind !== "trivial") {
|
|
104
|
+
systemPrompt += `\n${TASK_MANAGEMENT_SECTION}`;
|
|
105
|
+
}
|
|
102
106
|
|
|
103
|
-
if (
|
|
107
|
+
if (!open && kind === "multi_step") {
|
|
104
108
|
systemPrompt += `\n${COLD_START_BOOST}`;
|
|
105
109
|
pendingIntentNudge = buildColdStartReminder(prompt);
|
|
106
|
-
} else if (
|
|
110
|
+
} else if (open && kind === "completion") {
|
|
107
111
|
const openLines = getTodos().filter(isOpenTodo).map(formatPlainTodoLine);
|
|
108
112
|
pendingIntentNudge = buildCompletionUpdateReminder(openLines);
|
|
109
113
|
}
|
package/src/prompt-intent.ts
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* - Tool description alone is easy to ignore.
|
|
6
6
|
* - Idle reminders only fire when open work already exists — so cold start
|
|
7
7
|
* needs a separate, prompt-aware path.
|
|
8
|
-
* - We never invent todo items from chat
|
|
9
|
-
* todo_write when the ask is clearly multi-step.
|
|
8
|
+
* - We never invent todo items from chat.
|
|
10
9
|
*
|
|
11
|
-
* Bias:
|
|
10
|
+
* Bias: only nudge for clearly multi-step work; default to unknown for
|
|
11
|
+
* ambiguous or single-step requests.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
export type PromptIntent =
|
|
@@ -17,7 +17,7 @@ export type PromptIntent =
|
|
|
17
17
|
| { kind: "trivial"; reason: string }
|
|
18
18
|
| { kind: "unknown"; reason: string };
|
|
19
19
|
|
|
20
|
-
/** Explicit multi-step / planning verbs (EN + VI).
|
|
20
|
+
/** Explicit multi-step / planning verbs (EN + VI). */
|
|
21
21
|
const MULTI_STEP_VERBS =
|
|
22
22
|
/\b(explain|explore|review|walkthrough|overview|summarize|summarise|implement|refactor|audit|analyze|analyse|debug|investigate|migrate|redesign|rewrite|rebuild|build|create|add|fix|upgrade|improve|optimize|optimise|document|compare|research|triage|harden|ship|deploy|setup|configure|wire|polish|verify|validate|check|inspect|test|e2e|dogfood|install|remove|replace|integrate|extend|support|handle|track|persist|render|design|plan|giải\s*thích|khám\s*phá|rà\s*soát|triển\s*khai|refactor|sửa|làm|xây|kiểm\s*tra|phân\s*tích|cải\s*thiện|tối\s*ưu|bổ\s*sung|chỉnh|thiết\s*kế|cài|gỡ|xem|nghiên\s*cứu|đối\s*chiếu)\b/i;
|
|
23
23
|
|
|
@@ -33,7 +33,7 @@ const HELP_ASK =
|
|
|
33
33
|
const EXPLICIT_TODO =
|
|
34
34
|
/\b(todo|todos|task\s*list|checklist|break\s*(it|this)\s*down|step\s*by\s*step|multi[\s-]*step|kế\s*hoạch|danh\s*sách\s*việc)\b/i;
|
|
35
35
|
|
|
36
|
-
/** Numbered / bulleted multi-item asks. */
|
|
36
|
+
/** Numbered / bulleted multi-item asks (at least 2 items). */
|
|
37
37
|
const LIST_MARKERS = /(?:^|\n)\s*(?:\d+[\.\)]\s+\S|[-*]\s+\S)/;
|
|
38
38
|
|
|
39
39
|
/** Completion / done signals from the user. */
|
|
@@ -70,11 +70,14 @@ export function classifyPrompt(prompt: string): PromptIntent {
|
|
|
70
70
|
return { kind: "unknown", reason: "factoid" };
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
// Explicit planning requests with sufficient context.
|
|
74
|
+
if (EXPLICIT_TODO.test(text) && text.length >= 24) {
|
|
74
75
|
return { kind: "multi_step", reason: "explicit_todo" };
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
// Multi-item lists (≥2 bullets or numbered items).
|
|
79
|
+
const markers = text.match(/(?:^|\n)\s*(?:\d+[\.\)]\s+\S|[-*]\s+\S)/g);
|
|
80
|
+
if (markers && markers.length >= 2) {
|
|
78
81
|
return { kind: "multi_step", reason: "list_markers" };
|
|
79
82
|
}
|
|
80
83
|
|
|
@@ -84,34 +87,34 @@ export function classifyPrompt(prompt: string): PromptIntent {
|
|
|
84
87
|
return { kind: "multi_step", reason: "verb_and_scope" };
|
|
85
88
|
}
|
|
86
89
|
|
|
87
|
-
if (hasVerb && text.length >=
|
|
90
|
+
if (hasVerb && text.length >= 48) {
|
|
88
91
|
return { kind: "multi_step", reason: "verb_and_length" };
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
if (HELP_ASK.test(text) && text.length >=
|
|
94
|
+
if (HELP_ASK.test(text) && text.length >= 60) {
|
|
92
95
|
return { kind: "multi_step", reason: "help_ask" };
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
// Multiple clauses / sequenced work.
|
|
96
|
-
if (/\b(and then|then |after that|sau đó|rồi |đồng thời|also |và )\b/i.test(text) && text.length >=
|
|
99
|
+
if (/\b(and then|then |after that|sau đó|rồi |đồng thời|also |và )\b/i.test(text) && text.length >= 72) {
|
|
97
100
|
return { kind: "multi_step", reason: "sequenced_clauses" };
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
// Substantive paragraph with no strong verb still often needs a plan.
|
|
101
|
-
if (text.length >=
|
|
104
|
+
if (text.length >= 120 && !FACTOID.test(text)) {
|
|
102
105
|
return { kind: "multi_step", reason: "substantive_length" };
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
// Two+ sentences / newlines →
|
|
108
|
+
// Two+ sentences / newlines → sometimes multi-step instructions.
|
|
106
109
|
const sentenceBreaks = (text.match(/[.!?\n]/g) ?? []).length;
|
|
107
|
-
if (sentenceBreaks >=
|
|
110
|
+
if (sentenceBreaks >= 3 && text.length >= 80) {
|
|
108
111
|
return { kind: "multi_step", reason: "multi_sentence" };
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
return { kind: "unknown", reason: "no_strong_signal" };
|
|
112
115
|
}
|
|
113
116
|
|
|
114
|
-
/** True when
|
|
117
|
+
/** True when a cold-start todo reminder is relevant. */
|
|
115
118
|
export function shouldNudgeColdStart(prompt: string, hasOpenWork: boolean): boolean {
|
|
116
119
|
if (hasOpenWork) return false;
|
|
117
120
|
return classifyPrompt(prompt).kind === "multi_step";
|
|
@@ -126,11 +129,11 @@ export function shouldNudgeCompletionUpdate(prompt: string, hasOpenWork: boolean
|
|
|
126
129
|
export function buildColdStartReminder(prompt: string): string {
|
|
127
130
|
const clipped = prompt.trim().replace(/\s+/g, " ").slice(0, 160);
|
|
128
131
|
return `<system-reminder>
|
|
129
|
-
This
|
|
132
|
+
This request may involve multiple steps. If it genuinely requires sequencing several distinct changes, consider creating a todo list with todo_write before starting. Otherwise proceed directly.
|
|
130
133
|
|
|
131
134
|
User ask (clipped): "${clipped}"
|
|
132
135
|
|
|
133
|
-
|
|
136
|
+
NEVER mention this reminder to the user.
|
|
134
137
|
</system-reminder>`;
|
|
135
138
|
}
|
|
136
139
|
|
|
@@ -140,7 +143,7 @@ export function buildCompletionUpdateReminder(openLines: string[]): string {
|
|
|
140
143
|
? `\nOpen todos:\n${openLines.map((l) => `- ${l}`).join("\n")}\n`
|
|
141
144
|
: "\n";
|
|
142
145
|
return `<system-reminder>
|
|
143
|
-
The user signaled
|
|
146
|
+
The user may have signaled completion. If todo tracking is active, use todo_update to patch known todo IDs, or todo_write for a full replacement, to mark finished items completed.
|
|
144
147
|
${body}
|
|
145
148
|
NEVER mention this reminder to the user.
|
|
146
149
|
</system-reminder>`;
|
package/src/prompt.ts
CHANGED
|
@@ -1,86 +1,79 @@
|
|
|
1
|
-
export const TODOWRITE_DESCRIPTION = `Create and maintain a structured task list for the current coding session. Tracks progress, organizes multi-step work, and surfaces status in an overlay above the editor.
|
|
2
|
-
|
|
3
|
-
## When to Use This Tool
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
- \`
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
</
|
|
79
|
-
`;
|
|
80
|
-
|
|
81
|
-
/** Extra systemPrompt boost when prompt-intent detects multi-step + empty list. */
|
|
82
|
-
export const COLD_START_BOOST = `
|
|
83
|
-
<Task_Management_Priority>
|
|
84
|
-
COLD START ACTIVE: the current user message is multi-step and there is no open todo list. Call todo_write before any other tool. Prefer 3–8 concrete checklist items. Keep exactly one in_progress.
|
|
85
|
-
</Task_Management_Priority>
|
|
86
|
-
`;
|
|
1
|
+
export const TODOWRITE_DESCRIPTION = `Create and maintain a structured task list for the current coding session. Tracks progress, organizes multi-step work, and surfaces status in an overlay above the editor.
|
|
2
|
+
|
|
3
|
+
## When to Use This Tool
|
|
4
|
+
|
|
5
|
+
Good candidates for todo tracking:
|
|
6
|
+
|
|
7
|
+
- Work that involves 3+ distinct steps or touches multiple files
|
|
8
|
+
- A feature implementation, refactor, audit, or codebase walkthrough
|
|
9
|
+
- The user provides a numbered list or explicitly asks for planning
|
|
10
|
+
- You are tracking progress through sequenced changes
|
|
11
|
+
|
|
12
|
+
## When NOT to Use This Tool
|
|
13
|
+
|
|
14
|
+
Skip when:
|
|
15
|
+
- There is only a single, straightforward step (one file glance, one short answer)
|
|
16
|
+
- A one-line factual question with no multi-step plan
|
|
17
|
+
- Tracking truly adds no organizational value
|
|
18
|
+
- The request is simple even if it uses words like "explain" or "review"
|
|
19
|
+
|
|
20
|
+
## States
|
|
21
|
+
|
|
22
|
+
- \`pending\` — not started
|
|
23
|
+
- \`in_progress\` — actively working (exactly ONE at a time; the tool rejects >1)
|
|
24
|
+
- \`completed\` — finished successfully
|
|
25
|
+
- \`cancelled\` — no longer needed
|
|
26
|
+
|
|
27
|
+
## Rules
|
|
28
|
+
|
|
29
|
+
- Each call **REPLACES** the entire list (full replace). Always pass the complete todos array.
|
|
30
|
+
- **ID rule:** omit \`id\` for every new item — the system assigns a short ID (\`t1\`, \`t2\`, …). Only preserve an \`id\` returned by \`todo_read\` for an item that already exists; never invent an ID. For changed, repeated, or long/truncated content, preserve the exact existing ID instead of relying on content matching. Full replacement does not inherently reset IDs: matching existing items can retain them.
|
|
31
|
+
- Do not call \`todo_write\` and a dependent \`todo_update\` in the same parallel batch. Complete the write, then use its returned IDs (or \`todo_read\`) for the update.
|
|
32
|
+
- Update status in real time; don't batch completions across multiple finished steps.
|
|
33
|
+
- Mark \`completed\` only after the work is actually done (including verification) — never on intent alone.
|
|
34
|
+
- Keep exactly one \`in_progress\` while actively working. Never leave a stale \`in_progress\` after that step is finished.
|
|
35
|
+
- When using \`todo_write\`, after marking an item \`completed\`, set the next actionable item to \`in_progress\` in the same full-replace call when you are continuing.
|
|
36
|
+
- Array order is the workflow timeline. Preserve existing item positions as statuses change; add or reorder items only deliberately.
|
|
37
|
+
- If blocked, keep the active item \`in_progress\` and add a follow-up todo for the blocker.
|
|
38
|
+
- Items should be specific and actionable.
|
|
39
|
+
- Do not call todo_write and todo_read in the same parallel tool batch — write first, then read later if needed.
|
|
40
|
+
- Prefer the live overlay for status; use todo_read only when you need the JSON snapshot.`;
|
|
41
|
+
|
|
42
|
+
export const TODOREAD_DESCRIPTION =
|
|
43
|
+
"Read the current session todo list. Returns JSON of all todos with status, priority, and stable IDs. Prefer the overlay for at-a-glance status; call after todo_write settles (not in the same parallel batch). A legacy item without an ID cannot be patched by todo_update; rewrite it with todo_write and omit id to assign one.";
|
|
44
|
+
|
|
45
|
+
export const TODODIAGNOSE_DESCRIPTION =
|
|
46
|
+
"Read-only persistence diagnostic. Compares the current in-memory todo snapshot with the durable session-branch replay. Use only to investigate suspected reload, tree-navigation, or compaction state drift; it never changes todos.";
|
|
47
|
+
|
|
48
|
+
export const TODOWRITE_GUIDELINES = [
|
|
49
|
+
"For multi-step work (3+ steps) or when the user gives a list of tasks, todo_write helps track progress.",
|
|
50
|
+
"Pass the full list every todo_write call (full replace). Keep exactly one todo in_progress; mark completed immediately when a step finishes — never leave a stale in_progress.",
|
|
51
|
+
"ID rule: for todo_write, omit id for new items so the system assigns a short ID (t1, t2, …). Supply an id only to preserve an existing item, using the exact ID from todo_read; never invent IDs. For changed, repeated, or long/truncated content, preserve that exact ID rather than relying on content matching. For todo_update, id is required and must match a current todo.",
|
|
52
|
+
"Never call todo_write and a todo_update that depends on its IDs in the same parallel batch. Wait for todo_write to finish, then use its returned IDs or call todo_read. If todo_read shows a legacy item without id, rewrite it with todo_write and omit id to assign one before using todo_update.",
|
|
53
|
+
"When finishing a step or when the user confirms done, use todo_update for an ID-based patch when possible; use todo_write only when replacing the full checklist. Advance the next pending item in the same mutation if work continues.",
|
|
54
|
+
"Treat todo array order as the workflow timeline: preserve positions when updating statuses, and only add or reorder items intentionally.",
|
|
55
|
+
"Use todo_write for full replacement and todo_update for ID-based patches; todo_read exposes IDs and todo_diagnose is read-only troubleshooting.",
|
|
56
|
+
"Do not call todo_write and todo_read in the same parallel tool batch.",
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Appended to the system prompt for non-trivial asks (pi-todotools pattern).
|
|
61
|
+
* Fixes cold-start: tool description alone is easy for models to ignore.
|
|
62
|
+
* Keep short — token cost every turn.
|
|
63
|
+
*/
|
|
64
|
+
export const TASK_MANAGEMENT_SECTION = `
|
|
65
|
+
<Task_Management>
|
|
66
|
+
todo_write/todo_update/todo_read are the coordination layer for multi-step work. Use todo_write for the initial/full checklist and todo_update for a targeted patch by stable ID. In todo_write, omit id for new items (system assigns t1, t2, …); only retain an exact ID returned by todo_read for an existing item. In todo_update, id is required and must match a current todo. Do not call a write and an update that depends on its IDs in the same parallel batch: wait for the write result first. The TUI overlay only appears after a todo mutation — without it the user cannot see plan/progress.
|
|
67
|
+
|
|
68
|
+
When the user request is genuinely multi-step (multiple distinct files, sequenced changes, or an explicit task list), use todo_write to plan and track progress before implementing. For simple or single-step requests, skip planning and proceed directly.
|
|
69
|
+
|
|
70
|
+
Ongoing: if you are tracking work, mark completed immediately when a step finishes; advance the next pending item in the same mutation. When the user says done/approved, update that turn if applicable.
|
|
71
|
+
</Task_Management>
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
/** Extra systemPrompt boost when prompt-intent detects multi-step + empty list. */
|
|
75
|
+
export const COLD_START_BOOST = `
|
|
76
|
+
<Task_Management_Priority>
|
|
77
|
+
This request appears to involve multiple steps. If that is the case, consider creating a todo list with todo_write before starting. For single-step requests, proceed directly.
|
|
78
|
+
</Task_Management_Priority>
|
|
79
|
+
`;
|