pi-quests 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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.3.0] - 2026-04-11
6
+
7
+ - feat: add reorder action to move quests by position (dataplane, tool, /quests command, renderer, revert support)
8
+ - feat: extend clear action with optional all flag to remove all quests regardless of done state
9
+ - feat: detect appended completion markers (e.g. "- DONE") and inject correction nudge via context hook
10
+ - feat: update list rendering to show 1-based positions instead of raw IDs
11
+ - fix: inline type imports across test files to comply with no-inline-imports rule
12
+ - chore: extract shared prompt strings into prompts.ts to eliminate duplication
13
+ - chore: fix release workflow output mapping in create-release job
14
+
5
15
  ## [0.2.0] - 2026-04-11
6
16
 
7
17
  - feat: add dynamic quest usage nudges via context hook
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-quests
2
2
 
3
- [![version 0.2.0](https://img.shields.io/badge/version-0.2.0-blue)](CHANGELOG.md)
3
+ [![version 0.3.0](https://img.shields.io/badge/version-0.3.0-blue)](CHANGELOG.md)
4
4
  [![MIT license](https://img.shields.io/badge/license-MIT-green)](LICENSE.md)
5
5
  [![pi extension](https://img.shields.io/badge/pi-extension-purple)](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent)
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-quests",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A quest-log for your pi. Keep your agent on track, one quest at a time.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -17,6 +17,7 @@ type MutatingCommand = Extract<
17
17
  | typeof QUEST_ACTIONS.update
18
18
  | typeof QUEST_ACTIONS.delete
19
19
  | typeof QUEST_ACTIONS.clear
20
+ | typeof QUEST_ACTIONS.reorder
20
21
  | typeof QUEST_ACTIONS.revert;
21
22
  }
22
23
  >["action"];
@@ -32,7 +33,12 @@ const commandActionBuilders: {
32
33
  description: p.description,
33
34
  }),
34
35
  [QUEST_ACTIONS.delete]: (p) => ({ type: QUEST_ACTIONS.delete, id: p.id }),
35
- [QUEST_ACTIONS.clear]: () => ({ type: QUEST_ACTIONS.clear }),
36
+ [QUEST_ACTIONS.clear]: (p) => ({ type: QUEST_ACTIONS.clear, all: p.all }),
37
+ [QUEST_ACTIONS.reorder]: (p) => ({
38
+ type: QUEST_ACTIONS.reorder,
39
+ id: p.id,
40
+ targetIndex: p.targetIndex,
41
+ }),
36
42
  [QUEST_ACTIONS.revert]: () => ({ type: QUEST_ACTIONS.revert }),
37
43
  };
38
44
 
@@ -101,6 +107,7 @@ export function createQuestsHandler(pi: ExtensionAPI, questLog: QuestLog) {
101
107
  case QUEST_ACTIONS.update:
102
108
  case QUEST_ACTIONS.delete:
103
109
  case QUEST_ACTIONS.clear:
110
+ case QUEST_ACTIONS.reorder:
104
111
  case QUEST_ACTIONS.revert: {
105
112
  const builder = commandActionBuilders[parsed.action];
106
113
  const action = builder(parsed as never);
@@ -119,8 +126,9 @@ export function createQuestsHandler(pi: ExtensionAPI, questLog: QuestLog) {
119
126
  lines.push(" toggle <id> - Toggle quest completion");
120
127
  lines.push(" delete <id> - Delete a quest");
121
128
  lines.push(" update <id> <desc> - Update a quest description");
129
+ lines.push(" reorder <id> <idx> - Reorder a quest to index");
122
130
  lines.push(" revert - Revert the last quest change");
123
- lines.push(" clear - Clear all quests");
131
+ lines.push(" clear [all] - Clear completed quests, or optionally all quests");
124
132
  lines.push(" version - Show version");
125
133
  lines.push(" changelog - Show changelog");
126
134
  lines.push(" h, help - Show this help message");
@@ -7,7 +7,8 @@ export type ParsedArgs =
7
7
  | { action: typeof QUEST_ACTIONS.toggle; id: number }
8
8
  | { action: typeof QUEST_ACTIONS.update; id: number; description: string }
9
9
  | { action: typeof QUEST_ACTIONS.delete; id: number }
10
- | { action: typeof QUEST_ACTIONS.clear }
10
+ | { action: typeof QUEST_ACTIONS.clear; all?: boolean }
11
+ | { action: typeof QUEST_ACTIONS.reorder; id: number; targetIndex: number }
11
12
  | { action: typeof QUEST_ACTIONS.revert }
12
13
  | { action: "help" }
13
14
  | { action: "version" }
@@ -36,7 +37,7 @@ export function parseQuestArgs(args: string): ParsedArgs {
36
37
 
37
38
  // Quest actions without arguments
38
39
  if (command === QUEST_ACTIONS.list) return { action: QUEST_ACTIONS.list };
39
- if (command === QUEST_ACTIONS.clear) return { action: QUEST_ACTIONS.clear };
40
+ if (command === QUEST_ACTIONS.clear) return parseClearArgs(rest);
40
41
  if (command === QUEST_ACTIONS.revert) return { action: QUEST_ACTIONS.revert };
41
42
 
42
43
  // Quest actions with arguments
@@ -44,6 +45,7 @@ export function parseQuestArgs(args: string): ParsedArgs {
44
45
  if (command === QUEST_ACTIONS.toggle) return parseIdAction(QUEST_ACTIONS.toggle, rest);
45
46
  if (command === QUEST_ACTIONS.delete) return parseIdAction(QUEST_ACTIONS.delete, rest);
46
47
  if (command === QUEST_ACTIONS.update) return parseUpdateArgs(rest);
48
+ if (command === QUEST_ACTIONS.reorder) return parseReorderArgs(rest);
47
49
 
48
50
  return { error: `Unknown subcommand: ${command}. Use /quests help to see available commands.` };
49
51
  }
@@ -76,3 +78,17 @@ function parseUpdateArgs(tokens: string[]): ParsedArgs {
76
78
 
77
79
  return { action: QUEST_ACTIONS.update, id, description };
78
80
  }
81
+
82
+ function parseClearArgs(tokens: string[]): ParsedArgs {
83
+ const all = tokens[0] === "all";
84
+ if (tokens.length > 0 && !all) return { error: "Usage: /quests clear [all]" };
85
+ return { action: QUEST_ACTIONS.clear, all };
86
+ }
87
+
88
+ function parseReorderArgs(tokens: string[]): ParsedArgs {
89
+ const id = Number(tokens[0]);
90
+ const targetIndex = Number(tokens[1]);
91
+ if (Number.isNaN(id) || Number.isNaN(targetIndex))
92
+ return { error: "Usage: /quests reorder <id> <index>" };
93
+ return { action: QUEST_ACTIONS.reorder, id, targetIndex };
94
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import type { UserMessage } from "@mariozechner/pi-ai";
1
2
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
3
  import { createQuestsHandler } from "./commands/handler.js";
4
+ import { QUEST_PROMPT_GATE, QUEST_PROMPT_REMINDER } from "./prompts.js";
3
5
  import { QuestLog } from "./quest/dataplane.js";
4
6
  import { QuestUsageTracker } from "./quest/tracker.js";
5
7
  import { questChangelogRenderer } from "./renderers/changelog.js";
@@ -36,11 +38,20 @@ export default function (pi: ExtensionAPI): void {
36
38
 
37
39
  const activeQuestCount = questLog.getAll().filter((q) => !q.done).length;
38
40
  const nudge = tracker.getNudge(activeQuestCount, latestPrompt);
39
- if (!nudge) return undefined;
40
41
 
41
- const reminder: import("@mariozechner/pi-ai").UserMessage = {
42
+ const fakeDoneRegex =
43
+ /\s[-–—]\s*(DONE|COMPLETED|FINISHED)$|\s[([](DONE|COMPLETED|FINISHED)[)\]]$/i;
44
+ const fakeDone = questLog.getAll().find((q) => !q.done && fakeDoneRegex.test(q.description));
45
+ if (!nudge && !fakeDone) return undefined;
46
+
47
+ let content = nudge ?? "";
48
+ if (fakeDone) {
49
+ content += `\nQUEST REMINDER: Quest #${fakeDone.id} has a completion marker appended to its description but is not toggled done. Use the toggle action to mark it done. NEVER append completion markers to descriptions via the update action.`;
50
+ }
51
+
52
+ const reminder: UserMessage = {
42
53
  role: "user",
43
- content: nudge,
54
+ content: content.trim(),
44
55
  timestamp: Date.now(),
45
56
  };
46
57
  return { messages: [...event.messages, reminder] };
@@ -48,11 +59,7 @@ export default function (pi: ExtensionAPI): void {
48
59
 
49
60
  pi.on("before_agent_start", async (event) => {
50
61
  const quests = questLog.getAll();
51
- const gate =
52
- "# Quest Management\nBefore reading files, running commands, or making edits, ALWAYS ensure the current work is tracked as specific, actionable quests. ALWAYS break broad requests into concrete steps.\n\n";
53
-
54
- let reminder =
55
- "## Quest Management\nUse the quest tool VERY frequently to track tasks, plans, and progress throughout the conversation. It is critical that you toggle quests to done as soon as you complete them. NEVER batch up multiple tasks before marking them completed.\n\nNEVER create a single vague quest for broad requests. Analyze the user's intent and break it into specific, independent, actionable quests that each represent a concrete step.";
62
+ let reminder = QUEST_PROMPT_REMINDER.join("\n");
56
63
  if (quests.length > 0) {
57
64
  const remaining = quests.filter((q) => !q.done).length;
58
65
  const list = quests
@@ -62,7 +69,9 @@ export default function (pi: ExtensionAPI): void {
62
69
  reminder += `\n\nActive quests (${remaining}/${quests.length}):\n${list}`;
63
70
  }
64
71
 
65
- return { systemPrompt: `${gate}${event.systemPrompt}\n\n${reminder}` };
72
+ return {
73
+ systemPrompt: `# Quest Management\n${QUEST_PROMPT_GATE}${event.systemPrompt}\n\n## Quest Management\n${reminder}`,
74
+ };
66
75
  });
67
76
 
68
77
  registerQuestTool(pi, questLog);
package/src/prompts.ts ADDED
@@ -0,0 +1,16 @@
1
+ export const QUEST_PROMPT_REMINDER = [
2
+ "Use the quest tool VERY frequently to track tasks, plans, and progress throughout the conversation.",
3
+ "Before reading files, running commands, or making edits, ensure the current work is tracked as specific, actionable quests.",
4
+ "Do not create a single vague quest for broad requests. Break them into concrete, independent steps.",
5
+ "When the user gives a plan or a list of tasks, add them as quests immediately.",
6
+ "When reading a skill file, implementation plan, or protocol document that contains numbered steps or checklists, add those steps as quests immediately so they are tracked, and reorder them as needed in the quest log.",
7
+ "It is critical that you toggle quests to done as soon as you complete them. Do NOT batch completions.",
8
+ "ALWAYS use the toggle action to mark a quest done. NEVER use the update action to append 'DONE', '- DONE', or any completion marker to a quest description.",
9
+ "As work evolves, use the reorder action to reflect changes in priority or sequencing.",
10
+ "Before delegating to a minion, add a quest for the delegated task.",
11
+ "For reorder, targetIndex is 0-based (array index), not a 1-based position. If the user says 'move quest to position 5', use targetIndex 4.",
12
+ "If you are unsure what to do next, use the list action to check active quests.",
13
+ ] as const;
14
+
15
+ export const QUEST_PROMPT_GATE =
16
+ "Before reading files, running commands, or making edits, ALWAYS ensure the current work is tracked as specific, actionable quests. ALWAYS break broad requests into concrete steps.";
@@ -2,7 +2,6 @@ import type { AgentToolResult, ExtensionContext } from "@mariozechner/pi-coding-
2
2
  import { logger } from "../logger.js";
3
3
  import {
4
4
  formatBatchAddResult,
5
- formatClearResult,
6
5
  formatDeleteResult,
7
6
  formatNotFound,
8
7
  formatQuestList,
@@ -16,7 +15,14 @@ export type HistoryEntry =
16
15
  | { type: typeof QUEST_ACTIONS.toggle; id: number }
17
16
  | { type: typeof QUEST_ACTIONS.update; id: number; previousDescription: string }
18
17
  | { type: typeof QUEST_ACTIONS.delete; quest: Quest; index: number }
19
- | { type: typeof QUEST_ACTIONS.clear; quests: Quest[]; nextId: number };
18
+ | {
19
+ type: typeof QUEST_ACTIONS.clear;
20
+ previousQuests: Quest[];
21
+ previousNextId: number;
22
+ all: false;
23
+ }
24
+ | { type: typeof QUEST_ACTIONS.clear; quests: Quest[]; nextId: number; all: true }
25
+ | { type: typeof QUEST_ACTIONS.reorder; quest: Quest; oldIndex: number; previousIds: number[] };
20
26
 
21
27
  export type QuestAction =
22
28
  | { type: typeof QUEST_ACTIONS.add; descriptions?: string[] }
@@ -24,7 +30,8 @@ export type QuestAction =
24
30
  | { type: typeof QUEST_ACTIONS.toggle; id?: number }
25
31
  | { type: typeof QUEST_ACTIONS.update; id?: number; description?: string }
26
32
  | { type: typeof QUEST_ACTIONS.delete; id?: number }
27
- | { type: typeof QUEST_ACTIONS.clear }
33
+ | { type: typeof QUEST_ACTIONS.clear; all?: boolean }
34
+ | { type: typeof QUEST_ACTIONS.reorder; id?: number; targetIndex?: number }
28
35
  | { type: typeof QUEST_ACTIONS.revert };
29
36
 
30
37
  export type QuestOperationResult = { success: boolean; message: string; quest?: Quest };
@@ -88,12 +95,35 @@ export class QuestLog {
88
95
  return { success: true, message: `Reverted delete for quest #${entry.quest.id}` };
89
96
  },
90
97
  [QUEST_ACTIONS.clear]: (entry) => {
98
+ if ("previousQuests" in entry) {
99
+ const restoredCount = entry.previousQuests.length - this.quests.length;
100
+ this.quests = [...entry.previousQuests];
101
+ this.nextId = entry.previousNextId;
102
+
103
+ return {
104
+ success: true,
105
+ message: `Reverted clear (${restoredCount} quests restored)`,
106
+ };
107
+ }
108
+
91
109
  this.quests = [...entry.quests];
92
110
  this.nextId = entry.nextId;
93
111
 
94
- logger.debug("quests:state", "revert-clear", { count: entry.quests.length });
95
112
  return { success: true, message: `Reverted clear (${entry.quests.length} quests restored)` };
96
113
  },
114
+ [QUEST_ACTIONS.reorder]: (entry) => {
115
+ const currentIndex = this.quests.indexOf(entry.quest);
116
+ if (currentIndex === -1) return { success: false, message: "Reordered quest not found" };
117
+
118
+ this.quests.splice(currentIndex, 1);
119
+ this.quests.splice(entry.oldIndex, 0, entry.quest);
120
+ for (let i = 0; i < this.quests.length; i++) {
121
+ this.quests[i].id = entry.previousIds[i];
122
+ }
123
+
124
+ this.nextId = Math.max(...entry.previousIds, 0) + 1;
125
+ return { success: true, message: `Reverted reorder for quest #${entry.quest.id}` };
126
+ },
97
127
  };
98
128
 
99
129
  getAll(): Quest[] {
@@ -174,16 +204,63 @@ export class QuestLog {
174
204
  return quest;
175
205
  }
176
206
 
177
- clear(): number {
207
+ clear(all = false): number {
208
+ if (!all) {
209
+ const done = this.quests.filter((q) => q.done);
210
+ const previousQuests = this.quests.map((q) => ({ ...q }));
211
+ const previousNextId = this.nextId;
212
+ this.quests = this.quests.filter((q) => !q.done);
213
+ for (let i = 0; i < this.quests.length; i++) {
214
+ this.quests[i].id = i + 1;
215
+ }
216
+ this.nextId = this.quests.length + 1;
217
+ this.history.push({
218
+ type: QUEST_ACTIONS.clear,
219
+ previousQuests,
220
+ previousNextId,
221
+ all: false,
222
+ });
223
+ logger.debug("quests:state", QUEST_ACTIONS.clear, { count: done.length, all });
224
+ return done.length;
225
+ }
178
226
  const count = this.quests.length;
179
- this.history.push({ type: QUEST_ACTIONS.clear, quests: [...this.quests], nextId: this.nextId });
227
+ this.history.push({
228
+ type: QUEST_ACTIONS.clear,
229
+ quests: [...this.quests],
230
+ nextId: this.nextId,
231
+ all: true,
232
+ });
180
233
  this.quests = [];
181
234
  this.nextId = 1;
182
-
183
- logger.debug("quests:state", QUEST_ACTIONS.clear, { count });
235
+ logger.debug("quests:state", QUEST_ACTIONS.clear, { count, all });
184
236
  return count;
185
237
  }
186
238
 
239
+ reorder(id: number, targetIndex: number): Quest | undefined {
240
+ const idx = this.quests.findIndex((q) => q.id === id);
241
+ if (idx === -1) {
242
+ logger.debug("quests:state", "reorder-not-found", { id });
243
+ return undefined;
244
+ }
245
+
246
+ const previousIds = this.quests.map((q) => q.id);
247
+ const [quest] = this.quests.splice(idx, 1);
248
+ this.quests.splice(targetIndex, 0, quest);
249
+ for (let i = 0; i < this.quests.length; i++) {
250
+ this.quests[i].id = i + 1;
251
+ }
252
+
253
+ this.nextId = this.quests.length + 1;
254
+ this.history.push({ type: QUEST_ACTIONS.reorder, quest, oldIndex: idx, previousIds });
255
+ logger.debug("quests:state", QUEST_ACTIONS.reorder, {
256
+ id: quest.id,
257
+ targetIndex,
258
+ total: this.quests.length,
259
+ });
260
+
261
+ return quest;
262
+ }
263
+
187
264
  revert(): QuestOperationResult {
188
265
  const entry = this.history.pop();
189
266
  if (!entry) {
@@ -274,8 +351,24 @@ export class QuestLog {
274
351
  return { success: true, message: formatDeleteResult(q), quest: q };
275
352
  }
276
353
  case QUEST_ACTIONS.clear: {
277
- const count = this.clear();
278
- return { success: true, message: formatClearResult(count) };
354
+ const count = this.clear(action.all);
355
+ const message = action.all
356
+ ? `Cleared ${count} quests`
357
+ : `Cleared ${count} completed quests`;
358
+
359
+ return { success: true, message };
360
+ }
361
+ case QUEST_ACTIONS.reorder: {
362
+ if (action.id === undefined)
363
+ return { success: false, message: "Error: id is required for reorder action" };
364
+
365
+ if (action.targetIndex === undefined)
366
+ return { success: false, message: "Error: targetIndex is required for reorder action" };
367
+
368
+ const q = this.reorder(action.id, action.targetIndex);
369
+ if (!q) return { success: false, message: formatNotFound(action.id) };
370
+
371
+ return { success: true, message: `Reordered quest #${q.id}: ${q.description}`, quest: q };
279
372
  }
280
373
  case QUEST_ACTIONS.revert: {
281
374
  return this.revert();
@@ -2,7 +2,8 @@ export function formatQuestList(
2
2
  quests: { id: number; description: string; done: boolean }[],
3
3
  ): string {
4
4
  if (quests.length === 0) return "No quests.";
5
- return quests.map((q) => `#${q.id} [${q.done ? "x" : " "}] ${q.description}`).join("\n");
5
+
6
+ return quests.map((q, i) => `#${i + 1} [${q.done ? "x" : " "}] ${q.description}`).join("\n");
6
7
  }
7
8
 
8
9
  export function formatAddResult(q: { id: number; description: string }): string {
@@ -9,6 +9,7 @@ export const QUEST_ACTIONS = {
9
9
  update: "update",
10
10
  delete: "delete",
11
11
  clear: "clear",
12
+ reorder: "reorder",
12
13
  revert: "revert",
13
14
  } as const;
14
15
 
@@ -19,6 +20,7 @@ export const QUEST_ACTION_VALUES = [
19
20
  QUEST_ACTIONS.update,
20
21
  QUEST_ACTIONS.delete,
21
22
  QUEST_ACTIONS.clear,
23
+ QUEST_ACTIONS.reorder,
22
24
  QUEST_ACTIONS.revert,
23
25
  ] as const;
24
26
 
@@ -110,9 +110,11 @@ export class QuestListWidget {
110
110
 
111
111
  const start = this.page * this.pageSize;
112
112
  const pageQuests = this.quests.slice(start, start + this.pageSize);
113
- for (const q of pageQuests) {
113
+ for (let i = 0; i < pageQuests.length; i++) {
114
+ const q = pageQuests[i];
115
+ const pos = start + i + 1;
114
116
  const marker = q.done ? th.fg("success", " ✓ ") : th.fg("muted", " ○ ");
115
- const idStr = th.fg(q.done ? "dim" : "accent", `#${q.id}`);
117
+ const idStr = th.fg(q.done ? "dim" : "accent", `#${pos}`);
116
118
  const desc = q.done
117
119
  ? th.fg("dim", th.strikethrough(q.description))
118
120
  : th.fg("text", q.description);
@@ -46,15 +46,20 @@ export function renderQuestResult(
46
46
  isPartial: options.isPartial,
47
47
  });
48
48
  const details = result.details as Record<string, unknown> | undefined;
49
- const questsToRender = details?.displayQuests ?? details?.quests;
49
+ const allQuests =
50
+ (details?.quests as Array<{ id: number; description: string; done: boolean }> | undefined) ??
51
+ [];
52
+ const questsToRender = (details?.displayQuests ?? details?.quests) as
53
+ | Array<{ id: number; description: string; done: boolean }>
54
+ | undefined;
50
55
 
51
56
  if (Array.isArray(questsToRender) && questsToRender.length > 0) {
52
- const lines = (questsToRender as Array<{ id: number; description: string; done: boolean }>).map(
53
- (q) => {
54
- const marker = q.done ? theme.fg("success", "✓") : theme.fg("dim", "○");
55
- return `${marker} ${theme.fg("text", `#${q.id}`)} ${theme.fg("muted", q.description)}`;
56
- },
57
- );
57
+ const lines = questsToRender.map((q) => {
58
+ const pos = allQuests.findIndex((x) => x.id === q.id) + 1 || 1;
59
+ const marker = q.done ? theme.fg("success", "✓") : theme.fg("dim", "○");
60
+
61
+ return `${marker} ${theme.fg("text", `#${pos}`)} ${theme.fg("muted", q.description)}`;
62
+ });
58
63
 
59
64
  return new Text(lines.join("\n"), 0, 0);
60
65
  }
@@ -1,6 +1,7 @@
1
1
  import type { AgentToolResult, ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
2
  import type { Static } from "@sinclair/typebox";
3
3
  import { logger } from "../logger.js";
4
+ import { QUEST_PROMPT_GATE, QUEST_PROMPT_REMINDER } from "../prompts.js";
4
5
  import { makeToolResult, type QuestAction, type QuestLog } from "../quest/dataplane.js";
5
6
  import { QUEST_ACTIONS } from "../quest/types.js";
6
7
  import { renderQuestCall, renderQuestResult } from "../renderers/tools.js";
@@ -37,8 +38,15 @@ const toolHandlers: {
37
38
  [QUEST_ACTIONS.delete](questLog, params, toolCallId) {
38
39
  return runTool(questLog, toolCallId, { type: QUEST_ACTIONS.delete, id: params.id });
39
40
  },
40
- [QUEST_ACTIONS.clear](questLog, _params, toolCallId) {
41
- return runTool(questLog, toolCallId, { type: QUEST_ACTIONS.clear });
41
+ [QUEST_ACTIONS.clear](questLog, params, toolCallId) {
42
+ return runTool(questLog, toolCallId, { type: QUEST_ACTIONS.clear, all: params.all });
43
+ },
44
+ [QUEST_ACTIONS.reorder](questLog, params, toolCallId) {
45
+ return runTool(questLog, toolCallId, {
46
+ type: QUEST_ACTIONS.reorder,
47
+ id: params.id,
48
+ targetIndex: params.targetIndex,
49
+ });
42
50
  },
43
51
  [QUEST_ACTIONS.revert](questLog, _params, toolCallId) {
44
52
  return runTool(questLog, toolCallId, { type: QUEST_ACTIONS.revert });
@@ -83,14 +91,7 @@ export function registerQuestTool(pi: ExtensionAPI, questLog: QuestLog): void {
83
91
  description:
84
92
  "Manage the session quest log. Use this VERY frequently to track tasks, plans, and progress throughout the conversation.",
85
93
  promptSnippet: "Add, list, toggle, update, delete, clear, or revert quest items",
86
- promptGuidelines: [
87
- "Before reading files, running commands, or making edits, ensure the current work is tracked as specific, actionable quests.",
88
- "Do not create a single vague quest for broad requests. Break them into concrete, independent steps.",
89
- "When the user gives a plan or a list of tasks, add them as quests immediately.",
90
- "It is critical that you toggle quests to done as soon as you complete them. Do NOT batch completions.",
91
- "Before delegating to a minion, add a quest for the delegated task.",
92
- "If you are unsure what to do next, use the list action to check active quests.",
93
- ],
94
+ promptGuidelines: [...QUEST_PROMPT_GATE, ...QUEST_PROMPT_REMINDER],
94
95
  parameters: QuestParams,
95
96
  execute: (toolCallId, params, _signal, _onUpdate, _ctx) =>
96
97
  questToolExecute(questLog, toolCallId, params),
@@ -18,7 +18,18 @@ export const QuestParams = Type.Object({
18
18
  ),
19
19
  id: Type.Optional(
20
20
  Type.Number({
21
- description: "Quest ID (required for toggle, update, delete actions)",
21
+ description: "Quest ID (required for toggle, update, delete, reorder actions)",
22
+ }),
23
+ ),
24
+ targetIndex: Type.Optional(
25
+ Type.Number({
26
+ description:
27
+ "Target index for reorder action (0-based array index). If moving to the 3rd slot in the list, use 2.",
28
+ }),
29
+ ),
30
+ all: Type.Optional(
31
+ Type.Boolean({
32
+ description: "Clear all quests when true (defaults to clearing only completed quests)",
22
33
  }),
23
34
  ),
24
35
  });