pi-quests 0.6.1 → 0.7.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,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.7.0] - 2026-04-26
6
+
7
+ - feat: add batch toggle and batch delete support for quests (#2) — @pascal-de-ladurantaye
8
+ - feat: add redo action and rename revert to undo
9
+ - chore: migrate from taskfile to npm scripts and update dependencies
10
+
11
+ ## [0.6.2] - 2026-04-17
12
+
13
+ - fix: system prompt was too weak
14
+ - chore: add more roadmap items
15
+
5
16
  ## [0.6.1] - 2026-04-17
6
17
 
7
18
  - fix: make quest prompt injection static and cache-friendly with `nudges.enable` and `display.showStatus` toggles
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-quests
2
2
 
3
- [![version 0.6.1](https://img.shields.io/badge/version-0.6.1-blue)](CHANGELOG.md)
3
+ [![version 0.7.0](https://img.shields.io/badge/version-0.7.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.6.1",
3
+ "version": "0.7.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": [
@@ -35,22 +35,27 @@
35
35
  "@mariozechner/pi-ai": "*",
36
36
  "@mariozechner/pi-coding-agent": "*",
37
37
  "@mariozechner/pi-tui": "*",
38
- "@sinclair/typebox": "*"
38
+ "typebox": "*"
39
39
  },
40
40
  "devDependencies": {
41
- "@biomejs/biome": "^2.4.10",
42
- "@mariozechner/pi-coding-agent": "^0.63.2",
43
- "@mariozechner/pi-tui": "^0.63.0",
44
- "typescript": "^5.7.0",
45
- "vitest": "^3.0.0"
41
+ "@biomejs/biome": "^2.4.13",
42
+ "@mariozechner/pi-coding-agent": "^0.70.2",
43
+ "@mariozechner/pi-tui": "^0.70.2",
44
+ "typebox": "^1.1.33",
45
+ "typescript": "^6.0.3",
46
+ "vitest": "^4.1.5"
46
47
  },
47
48
  "scripts": {
48
49
  "test": "vitest run",
49
50
  "test:watch": "vitest",
50
51
  "typecheck": "tsc --noEmit",
51
- "style": "biome check ./src ./test",
52
- "style:fix": "biome check --write --unsafe --no-errors-on-unmatched ./src ./test",
52
+ "style": "biome check --error-on-warnings ./src ./test",
53
+ "style:fix": "biome check --write --unsafe --no-errors-on-unmatched --error-on-warnings ./src ./test",
53
54
  "format": "biome format --write --no-errors-on-unmatched ./src ./test",
54
- "prepublishOnly": "npx tsc --noEmit && npx vitest run"
55
+ "prepublishOnly": "npx tsc --noEmit && npx vitest run",
56
+ "prepare": "bash scripts/setup.sh",
57
+ "cleanup": "bash scripts/cleanup.sh",
58
+ "dev": "bash scripts/dev.sh",
59
+ "logs": "bash scripts/logs.sh"
55
60
  }
56
61
  }
@@ -25,7 +25,8 @@ type MutatingCommand = Extract<
25
25
  | typeof QUEST_ACTIONS.clear
26
26
  | typeof QUEST_ACTIONS.reorder
27
27
  | typeof QUEST_ACTIONS.reparent
28
- | typeof QUEST_ACTIONS.revert;
28
+ | typeof QUEST_ACTIONS.undo
29
+ | typeof QUEST_ACTIONS.redo;
29
30
  }
30
31
  >["action"];
31
32
 
@@ -41,13 +42,13 @@ const commandActionBuilders: {
41
42
  id: p.id,
42
43
  descriptions: p.descriptions,
43
44
  }),
44
- [QUEST_ACTIONS.toggle]: (p) => ({ type: QUEST_ACTIONS.toggle, id: p.id }),
45
+ [QUEST_ACTIONS.toggle]: (p) => ({ type: QUEST_ACTIONS.toggle, ids: p.ids }),
45
46
  [QUEST_ACTIONS.update]: (p) => ({
46
47
  type: QUEST_ACTIONS.update,
47
48
  id: p.id,
48
49
  description: p.description,
49
50
  }),
50
- [QUEST_ACTIONS.delete]: (p) => ({ type: QUEST_ACTIONS.delete, id: p.id }),
51
+ [QUEST_ACTIONS.delete]: (p) => ({ type: QUEST_ACTIONS.delete, ids: p.ids }),
51
52
  [QUEST_ACTIONS.clear]: (p) => ({ type: QUEST_ACTIONS.clear, all: p.all }),
52
53
  [QUEST_ACTIONS.reorder]: (p) => ({
53
54
  type: QUEST_ACTIONS.reorder,
@@ -59,7 +60,8 @@ const commandActionBuilders: {
59
60
  id: p.id,
60
61
  parentId: p.parentId,
61
62
  }),
62
- [QUEST_ACTIONS.revert]: () => ({ type: QUEST_ACTIONS.revert }),
63
+ [QUEST_ACTIONS.undo]: () => ({ type: QUEST_ACTIONS.undo }),
64
+ [QUEST_ACTIONS.redo]: () => ({ type: QUEST_ACTIONS.redo }),
63
65
  };
64
66
 
65
67
  export function openQuestList(
@@ -143,7 +145,8 @@ export function createQuestsHandler(
143
145
  case QUEST_ACTIONS.clear:
144
146
  case QUEST_ACTIONS.reorder:
145
147
  case QUEST_ACTIONS.reparent:
146
- case QUEST_ACTIONS.revert: {
148
+ case QUEST_ACTIONS.undo:
149
+ case QUEST_ACTIONS.redo: {
147
150
  const builder = commandActionBuilders[parsed.action];
148
151
  const action = builder(parsed as never);
149
152
  const result = questLog.execute(action);
@@ -161,12 +164,13 @@ export function createQuestsHandler(
161
164
  lines.push(" add <description> - Add a new top-level quest");
162
165
  lines.push(" add-step <id> <description> - Split a quest into a step");
163
166
  lines.push(" list - List all quests");
164
- lines.push(" toggle <id> - Toggle quest completion");
165
- lines.push(" delete <id> - Delete a quest");
167
+ lines.push(" toggle <id> [moreIds...] - Toggle one or more quests");
168
+ lines.push(" delete <id> [moreIds...] - Delete one or more quests");
166
169
  lines.push(" update <id> <desc> - Update a quest description");
167
170
  lines.push(" reparent <id> [parentId] - Reparent a quest/step (omit parentId to promote)");
168
171
  lines.push(" reorder <id> <targetId> - Reorder a quest before the target quest");
169
- lines.push(" revert - Revert the last quest change");
172
+ lines.push(" undo - Undo the last quest change");
173
+ lines.push(" redo - Redo the last undone action");
170
174
  lines.push(" clear [all] - Clear completed quests, or optionally all quests");
171
175
  lines.push(" version - Show version");
172
176
  lines.push(" changelog - Show changelog");
@@ -5,12 +5,13 @@ export type ParsedArgs =
5
5
  | { action: typeof QUEST_ACTIONS.add; descriptions: string[] }
6
6
  | { action: typeof QUEST_ACTIONS.split; id: string; descriptions: string[] }
7
7
  | { action: typeof QUEST_ACTIONS.list }
8
- | { action: typeof QUEST_ACTIONS.toggle; id: string }
8
+ | { action: typeof QUEST_ACTIONS.toggle; ids: string[] }
9
9
  | { action: typeof QUEST_ACTIONS.update; id: string; description: string }
10
- | { action: typeof QUEST_ACTIONS.delete; id: string }
10
+ | { action: typeof QUEST_ACTIONS.delete; ids: string[] }
11
11
  | { action: typeof QUEST_ACTIONS.clear; all?: boolean }
12
12
  | { action: typeof QUEST_ACTIONS.reorder; id: string; targetId: string }
13
- | { action: typeof QUEST_ACTIONS.revert }
13
+ | { action: typeof QUEST_ACTIONS.undo }
14
+ | { action: typeof QUEST_ACTIONS.redo }
14
15
  | { action: typeof QUEST_ACTIONS.reparent; id: string; parentId?: string }
15
16
  | { action: "help" }
16
17
  | { action: "version" }
@@ -40,13 +41,14 @@ export function parseQuestArgs(args: string, idLength = 2): ParsedArgs {
40
41
  // Quest actions without arguments
41
42
  if (command === QUEST_ACTIONS.list) return { action: QUEST_ACTIONS.list };
42
43
  if (command === QUEST_ACTIONS.clear) return parseClearArgs(rest);
43
- if (command === QUEST_ACTIONS.revert) return { action: QUEST_ACTIONS.revert };
44
+ if (command === QUEST_ACTIONS.undo) return { action: QUEST_ACTIONS.undo };
45
+ if (command === QUEST_ACTIONS.redo) return { action: QUEST_ACTIONS.redo };
44
46
 
45
47
  // Quest actions with arguments
46
48
  if (command === QUEST_ACTIONS.add) return parseAddArgs(rest);
47
49
  if (command === "add-step") return parseAddStepArgs(rest, idLength);
48
- if (command === QUEST_ACTIONS.toggle) return parseIdAction(QUEST_ACTIONS.toggle, rest, idLength);
49
- if (command === QUEST_ACTIONS.delete) return parseIdAction(QUEST_ACTIONS.delete, rest, idLength);
50
+ if (command === QUEST_ACTIONS.toggle) return parseToggleArgs(rest, idLength);
51
+ if (command === QUEST_ACTIONS.delete) return parseDeleteArgs(rest, idLength);
50
52
  if (command === QUEST_ACTIONS.update) return parseUpdateArgs(rest, idLength);
51
53
  if (command === QUEST_ACTIONS.reorder) return parseReorderArgs(rest, idLength);
52
54
  if (command === QUEST_ACTIONS.reparent) return parseReparentArgs(rest, idLength);
@@ -70,16 +72,26 @@ function parseAddStepArgs(tokens: string[], idLength: number): ParsedArgs {
70
72
  return { action: QUEST_ACTIONS.split, id, descriptions: [description] };
71
73
  }
72
74
 
73
- function parseIdAction(
74
- action: typeof QUEST_ACTIONS.toggle | typeof QUEST_ACTIONS.delete,
75
- tokens: string[],
76
- idLength: number,
77
- ): ParsedArgs {
78
- const id = tokens[0] ? tokens[0].toLowerCase() : "";
75
+ function parseToggleArgs(tokens: string[], idLength: number): ParsedArgs {
79
76
  const pattern = new RegExp(`^[0-9a-f]{${idLength}}$`);
80
- if (!pattern.test(id)) return { error: `Usage: /quests ${action} <id>` };
77
+ const ids = tokens.map((token) => token.toLowerCase());
78
+
79
+ if (ids.length === 0 || ids.some((id) => !pattern.test(id))) {
80
+ return { error: "Usage: /quests toggle <id> [moreIds...]" };
81
+ }
82
+
83
+ return { action: QUEST_ACTIONS.toggle, ids };
84
+ }
85
+
86
+ function parseDeleteArgs(tokens: string[], idLength: number): ParsedArgs {
87
+ const pattern = new RegExp(`^[0-9a-f]{${idLength}}$`);
88
+ const ids = tokens.map((token) => token.toLowerCase());
89
+
90
+ if (ids.length === 0 || ids.some((id) => !pattern.test(id))) {
91
+ return { error: "Usage: /quests delete <id> [moreIds...]" };
92
+ }
81
93
 
82
- return { action, id };
94
+ return { action: QUEST_ACTIONS.delete, ids };
83
95
  }
84
96
 
85
97
  function parseUpdateArgs(tokens: string[], idLength: number): ParsedArgs {
package/src/index.ts CHANGED
@@ -22,7 +22,7 @@ import { registerQuestTool } from "./tools/handler.js";
22
22
  * - Provide rollback support to restore a previous snapshot.
23
23
  */
24
24
  const NO_QUESTS_REMINDER =
25
- "No active quests. Use the quest tool to track your work. Use action: 'skill' for usage documentation.";
25
+ "No active quests. Use the learn_quests tool to understand how to use quests and use quests to track this task. DO THIS NOW and acknowledge it.";
26
26
 
27
27
  const FAKE_DONE_REMINDER =
28
28
  "QUEST REMINDER: A quest has a completion marker appended to its description but is not toggled done. Use the list action to find it, then toggle it done. NEVER append completion markers via update.";
@@ -121,7 +121,11 @@ export default function (pi: ExtensionAPI): void {
121
121
  });
122
122
 
123
123
  pi.on("before_agent_start", async (event) => {
124
- if (tracker.hasUsedQuestTool) return undefined;
124
+ if (questLog.getAll().length === 0)
125
+ return {
126
+ systemPrompt: `${event.systemPrompt}\n\nUse quests to track this task. DO THIS now and acknowledge it.`,
127
+ };
128
+
125
129
  return {
126
130
  systemPrompt: `# Quest Management\n${QUEST_PROMPT_GATE}${event.systemPrompt}\n\n## Quest Management\n${NO_QUESTS_REMINDER}`,
127
131
  };
@@ -5,56 +5,55 @@ description: Quest management best practices for the pi-quests extension. Use wh
5
5
 
6
6
  # Quest Management
7
7
 
8
- ## When to use quests
9
-
10
- Use the quest tool at the start of any non-trivial task. If the request involves multiple steps, files, tool calls, or minion delegation, track it with quests.
11
-
12
- ## Capabilities
13
-
14
- | Action | Purpose |
15
- |--------|---------|
16
- | `add` | Create top-level quests |
17
- | `split` / `add_step` | Break a quest into steps under a parent |
18
- | `reparent` | Promote, demote, or move a quest/step via optional `parentId` |
19
- | `toggle` | Mark a quest or step done/undone |
20
- | `update` | Change a quest description |
21
- | `delete` | Remove a quest or step |
22
- | `clear` | Remove completed quests (or all with `all: true`) |
23
- | `reorder` | Change the priority order of top-level quests |
24
- | `revert` | Undo the most recent mutating action |
25
- | `list` | View all quests and steps |
26
-
27
- ## Common patterns
28
-
29
- ### Multi-step task workflow
30
- 1. Add many top-level quests for the overall goal
31
- 2. Split them into steps for each distinct deliverable
32
- 3. Execute steps sequentially, toggling each done as you finish
33
- 4. Toggle the parent quest done only after all steps are complete
34
-
35
- ### Delegation workflow
36
- 1. Add a quest for the delegated task
37
- 2. Spawn the minion and assign the work
38
- 3. Toggle the quest done when the minion returns successfully
8
+ This skill provides guidelines and best practices for using the quest management system in the pi-quests extension. It covers the core actions available for managing quests, recommended workflows for common use cases, and important rules to follow for effective task tracking and progress management.
9
+
10
+ ## Action Reference
11
+
12
+ | Action | Required Params | Behavior |
13
+ |--------|----------------|----------|
14
+ | `add` | `descriptions[]` | Create top-level quests. Batch multiple descriptions in one call. |
15
+ | `split` | `id`, `descriptions[]` | Break a quest into steps under it. Alias: `add_step`. |
16
+ | `list` | | View all quests and steps. |
17
+ | `toggle` | `id` | Flip done status. Blocked if parent has incomplete steps. |
18
+ | `update` | `id`, `description` | Change description. |
19
+ | `delete` | `id` | Remove a quest. Blocked if parent has incomplete steps. Cascade-deletes done steps of a done parent. |
20
+ | `clear` | | Remove completed quests. `all: true` removes everything. |
21
+ | `reorder` | `id`, `targetId` | Move a top-level quest before `targetId`. Steps cannot be reordered. |
22
+ | `reparent` | `id`, `parentId?` | Demote to step under `parentId`, or promote to top-level if `parentId` omitted. |
23
+ | `undo` | | Undo the most recent mutating action. One level only. |
24
+ | `redo` | | Redo the most recently undone action. |
25
+ | `rules` / `skill` | | Return this document. |
26
+
27
+ ## Workflows
28
+
29
+ ### Multi-step task
30
+ 1. `add` top-level quests for the overall goal
31
+ 2. `split` them into steps for each deliverable
32
+ 3. Execute steps sequentially, `toggle` each done as you finish
33
+ 4. `add` other top-level quests for other related work of this goal
34
+ 4. `toggle` the parent done only after all steps are complete
35
+
36
+ ### Delegation
37
+ 1. `add` a quest for the delegated task
38
+ 2. `spawn` the minion and assign the work
39
+ 3. `toggle` the quest done when the minion returns successfully
39
40
 
40
41
  ### Reparenting
41
42
  - Promote a step: `reparent <step-id>` (omit `parentId`)
42
43
  - Demote a quest: `reparent <quest-id> <parent-id>`
43
44
  - Move a step: `reparent <step-id> <new-parent-id>`
44
45
 
45
- ## Rules
46
-
47
- - Steps cannot have nested steps. Only top-level quests can be parents.
48
- - A parent with incomplete steps cannot be toggled done or deleted.
49
- - Deleting a done parent cascade-deletes its done steps.
50
- - Revert only undoes the most recent mutating action.
51
- - Quest IDs are random hex strings shown in square brackets (e.g., `[01]`, `[a3f1]`).
52
- - Always use the hex ID for actions, never the positional number.
46
+ ### Cleanup
47
+ - `clear` completed quests before adding unrelated work. Keeps the log focused.
53
48
 
54
- ## Gotchas
49
+ ## Rules
55
50
 
56
- - Use toggle for done states. NEVER use `update` to append "DONE", "- DONE", or any completion marker to a quest description.
57
- - IDs are hex, not positional. The list shows `#1 [01] ...`. Use `01` in tool calls, not `1`.
58
- - Steps cannot be reordered independently. Use `reorder` on top-level quests only.
59
- - Clear completed quests before adding unrelated work. This keeps the log focused and reduces context noise.
60
- - Parent blocked?: If you cannot toggle a parent done, check that all its steps are toggled done first.
51
+ - ALWAYS use quests for any work that involves multiple turns.
52
+ - NEVER try to nest steps. Only top-level quests can be parents.
53
+ - NEVER mark a parent as done if they have incomplete steps.
54
+ - NEVER delete a quest with incomplete steps.
55
+ - Use undo if you make a mistake to undo an action.
56
+ - Use redo to replay an action you just undid. Redo is cleared when a new mutation happens.
57
+ - ALWAYS use the hex ID, never the positional number when referring to quests in actions.
58
+ - ALWAYS Use `toggle` for done states. NEVER use `update` to append "DONE" or any completion marker to a description.
59
+ - If a parent toggle is blocked, check that all its steps are done first.