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 +11 -0
- package/README.md +1 -1
- package/package.json +15 -10
- package/src/commands/handler.ts +12 -8
- package/src/commands/parse-args.ts +26 -14
- package/src/index.ts +6 -2
- package/src/prompts/skill.md +44 -45
- package/src/quest/dataplane.ts +441 -114
- package/src/quest/formatters.ts +16 -2
- package/src/quest/types.ts +4 -2
- package/src/renderers/tools.ts +29 -8
- package/src/tools/handler.ts +95 -65
- package/src/tools/params.ts +8 -2
package/src/quest/formatters.ts
CHANGED
|
@@ -44,6 +44,12 @@ export function formatToggleResult(id: string, done: boolean): string {
|
|
|
44
44
|
return `Quest [${id}] ${done ? "done" : "undone"}`;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
export function formatBatchToggleResult(toggled: { id: string; done: boolean }[]): string {
|
|
48
|
+
return `Toggled ${toggled.length} tasks:\n${toggled
|
|
49
|
+
.map((quest) => `[${quest.id}] ${quest.done ? "done" : "undone"}`)
|
|
50
|
+
.join("\n")}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
export function formatUpdateResult(q: { id: string; description: string }): string {
|
|
48
54
|
return `Updated quest [${q.id}]: ${q.description}`;
|
|
49
55
|
}
|
|
@@ -52,6 +58,10 @@ export function formatDeleteResult(q: { id: string; description: string }): stri
|
|
|
52
58
|
return `Deleted quest [${q.id}]: ${q.description}`;
|
|
53
59
|
}
|
|
54
60
|
|
|
61
|
+
export function formatBatchDeleteResult(deleted: { id: string; description: string }[]): string {
|
|
62
|
+
return `Deleted ${deleted.length} tasks:\n${deleted.map((quest) => `[${quest.id}] ${quest.description}`).join("\n")}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
55
65
|
export function formatNotFound(id: string): string {
|
|
56
66
|
return `Quest [${id}] not found. IDs are random hex strings shown in brackets. Use the list action to see valid IDs.`;
|
|
57
67
|
}
|
|
@@ -100,8 +110,12 @@ export function formatUnknownActionError(action: string): string {
|
|
|
100
110
|
return `Unknown action: ${action}. Use the list action or check the tool schema for supported actions.`;
|
|
101
111
|
}
|
|
102
112
|
|
|
103
|
-
export function
|
|
104
|
-
return `Nothing to
|
|
113
|
+
export function formatNothingToUndoError(): string {
|
|
114
|
+
return `Nothing to undo. The history is empty because no mutating actions have been performed yet.`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function formatNothingToRedoError(): string {
|
|
118
|
+
return `Nothing to redo. The redo stack is empty because no actions have been undone yet.`;
|
|
105
119
|
}
|
|
106
120
|
|
|
107
121
|
export function formatReorderedQuestNotFoundError(): string {
|
package/src/quest/types.ts
CHANGED
|
@@ -10,7 +10,8 @@ export const QUEST_ACTIONS = {
|
|
|
10
10
|
delete: "delete",
|
|
11
11
|
clear: "clear",
|
|
12
12
|
reorder: "reorder",
|
|
13
|
-
|
|
13
|
+
undo: "undo",
|
|
14
|
+
redo: "redo",
|
|
14
15
|
reparent: "reparent",
|
|
15
16
|
rules: "rules",
|
|
16
17
|
skill: "skill",
|
|
@@ -26,7 +27,8 @@ export const QUEST_ACTION_VALUES = [
|
|
|
26
27
|
QUEST_ACTIONS.delete,
|
|
27
28
|
QUEST_ACTIONS.clear,
|
|
28
29
|
QUEST_ACTIONS.reorder,
|
|
29
|
-
QUEST_ACTIONS.
|
|
30
|
+
QUEST_ACTIONS.undo,
|
|
31
|
+
QUEST_ACTIONS.redo,
|
|
30
32
|
QUEST_ACTIONS.reparent,
|
|
31
33
|
QUEST_ACTIONS.rules,
|
|
32
34
|
QUEST_ACTIONS.skill,
|
package/src/renderers/tools.ts
CHANGED
|
@@ -11,6 +11,7 @@ type QuestArgs = {
|
|
|
11
11
|
descriptions?: string[];
|
|
12
12
|
targetId?: string;
|
|
13
13
|
id?: string;
|
|
14
|
+
ids?: string[];
|
|
14
15
|
all?: boolean;
|
|
15
16
|
};
|
|
16
17
|
|
|
@@ -31,12 +32,29 @@ export function renderQuestCall(args: QuestArgs, theme: Theme, _context: unknown
|
|
|
31
32
|
);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
if (
|
|
35
|
-
(args.
|
|
36
|
-
args.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
if (args.action === QUEST_ACTIONS.toggle) {
|
|
36
|
+
if (args.ids && args.ids.length > 1) {
|
|
37
|
+
return new Text(`${actionText} ${theme.fg("muted", `[${args.ids.length} tasks]`)}`, 0, 0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const toggleId = args.id ?? args.ids?.[0];
|
|
41
|
+
if (toggleId !== undefined) {
|
|
42
|
+
return new Text(`${actionText} ${theme.fg("muted", `[${toggleId}]`)}`, 0, 0);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (args.action === QUEST_ACTIONS.delete) {
|
|
47
|
+
if (args.ids && args.ids.length > 1) {
|
|
48
|
+
return new Text(`${actionText} ${theme.fg("muted", `[${args.ids.length} tasks]`)}`, 0, 0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const deleteId = args.id ?? args.ids?.[0];
|
|
52
|
+
if (deleteId !== undefined) {
|
|
53
|
+
return new Text(`${actionText} ${theme.fg("muted", `[${deleteId}]`)}`, 0, 0);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (args.action === QUEST_ACTIONS.update && args.id !== undefined) {
|
|
40
58
|
return new Text(`${actionText} ${theme.fg("muted", `[${args.id}]`)}`, 0, 0);
|
|
41
59
|
}
|
|
42
60
|
|
|
@@ -59,8 +77,11 @@ export function renderQuestResult(config: ResolvedConfig) {
|
|
|
59
77
|
isPartial: options.isPartial,
|
|
60
78
|
});
|
|
61
79
|
const details = result.details as Record<string, unknown> | undefined;
|
|
62
|
-
const
|
|
63
|
-
const
|
|
80
|
+
const snapshotQuests = details?.snapshotQuests as Quest[] | undefined;
|
|
81
|
+
const allQuests = (snapshotQuests ?? details?.quests) as Quest[] | undefined;
|
|
82
|
+
const questsToRender = (snapshotQuests ?? details?.displayQuests ?? details?.quests) as
|
|
83
|
+
| Quest[]
|
|
84
|
+
| undefined;
|
|
64
85
|
|
|
65
86
|
if (
|
|
66
87
|
!Array.isArray(questsToRender) ||
|
package/src/tools/handler.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import type { AgentToolResult, ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import type
|
|
1
|
+
import type { AgentToolResult, ExtensionAPI, Theme } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { type Static, Type } from "typebox";
|
|
3
3
|
import type { ResolvedConfig } from "../config.js";
|
|
4
4
|
import { logger } from "../logger.js";
|
|
5
5
|
import { QUEST_PROMPT_GATE, QUEST_PROMPT_REMINDER } from "../prompts.js";
|
|
6
6
|
import { makeToolResult, type QuestAction, type QuestLog } from "../quest/dataplane.js";
|
|
7
|
-
import { QUEST_ACTIONS } from "../quest/types.js";
|
|
7
|
+
import { QUEST_ACTIONS, type QuestActionType } from "../quest/types.js";
|
|
8
8
|
|
|
9
9
|
const SPLIT_DISPLAY_ACTIONS = [
|
|
10
10
|
QUEST_ACTIONS.add,
|
|
11
11
|
QUEST_ACTIONS.list,
|
|
12
12
|
QUEST_ACTIONS.split,
|
|
13
13
|
QUEST_ACTIONS.add_step,
|
|
14
|
-
QUEST_ACTIONS.
|
|
14
|
+
QUEST_ACTIONS.undo,
|
|
15
|
+
QUEST_ACTIONS.redo,
|
|
15
16
|
] as const;
|
|
16
17
|
|
|
17
18
|
import { invalidateQuestListWidget } from "../renderers/commands.js";
|
|
@@ -20,75 +21,72 @@ import { createQuestParams, type QuestParamsType } from "./params.js";
|
|
|
20
21
|
|
|
21
22
|
type QuestToolParams = Static<QuestParamsType>;
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
[QUEST_ACTIONS.add](questLog, params, toolCallId)
|
|
31
|
-
|
|
24
|
+
type ToolHandler = (
|
|
25
|
+
questLog: QuestLog,
|
|
26
|
+
params: QuestToolParams,
|
|
27
|
+
toolCallId: string,
|
|
28
|
+
) => AgentToolResult<unknown>;
|
|
29
|
+
|
|
30
|
+
const toolHandlers: Record<QuestActionType, ToolHandler> = {
|
|
31
|
+
[QUEST_ACTIONS.add]: (questLog, params, toolCallId) =>
|
|
32
|
+
runTool(questLog, toolCallId, {
|
|
32
33
|
type: QUEST_ACTIONS.add,
|
|
33
34
|
descriptions: params.descriptions,
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return runTool(questLog, toolCallId, {
|
|
35
|
+
}),
|
|
36
|
+
[QUEST_ACTIONS.split]: (questLog, params, toolCallId) =>
|
|
37
|
+
runTool(questLog, toolCallId, {
|
|
38
38
|
type: QUEST_ACTIONS.split,
|
|
39
39
|
id: params.id,
|
|
40
40
|
descriptions: params.descriptions,
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return runTool(questLog, toolCallId, {
|
|
41
|
+
}),
|
|
42
|
+
[QUEST_ACTIONS.add_step]: (questLog, params, toolCallId) =>
|
|
43
|
+
runTool(questLog, toolCallId, {
|
|
45
44
|
type: QUEST_ACTIONS.add_step,
|
|
46
45
|
id: params.id,
|
|
47
46
|
descriptions: params.descriptions,
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
}),
|
|
48
|
+
[QUEST_ACTIONS.list]: (questLog, _params, toolCallId) =>
|
|
49
|
+
runTool(questLog, toolCallId, { type: QUEST_ACTIONS.list }),
|
|
50
|
+
[QUEST_ACTIONS.toggle]: (questLog, params, toolCallId) =>
|
|
51
|
+
runTool(questLog, toolCallId, {
|
|
52
|
+
type: QUEST_ACTIONS.toggle,
|
|
53
|
+
id: params.id,
|
|
54
|
+
ids: params.ids,
|
|
55
|
+
}),
|
|
56
|
+
[QUEST_ACTIONS.update]: (questLog, params, toolCallId) =>
|
|
57
|
+
runTool(questLog, toolCallId, {
|
|
58
58
|
type: QUEST_ACTIONS.update,
|
|
59
59
|
id: params.id,
|
|
60
60
|
description: params.description,
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
}),
|
|
62
|
+
[QUEST_ACTIONS.delete]: (questLog, params, toolCallId) =>
|
|
63
|
+
runTool(questLog, toolCallId, {
|
|
64
|
+
type: QUEST_ACTIONS.delete,
|
|
65
|
+
id: params.id,
|
|
66
|
+
ids: params.ids,
|
|
67
|
+
}),
|
|
68
|
+
[QUEST_ACTIONS.clear]: (questLog, params, toolCallId) =>
|
|
69
|
+
runTool(questLog, toolCallId, { type: QUEST_ACTIONS.clear, all: params.all }),
|
|
70
|
+
[QUEST_ACTIONS.reorder]: (questLog, params, toolCallId) =>
|
|
71
|
+
runTool(questLog, toolCallId, {
|
|
71
72
|
type: QUEST_ACTIONS.reorder,
|
|
72
73
|
id: params.id,
|
|
73
74
|
targetId: params.targetId,
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return runTool(questLog, toolCallId, {
|
|
75
|
+
}),
|
|
76
|
+
[QUEST_ACTIONS.reparent]: (questLog, params, toolCallId) =>
|
|
77
|
+
runTool(questLog, toolCallId, {
|
|
78
78
|
type: QUEST_ACTIONS.reparent,
|
|
79
79
|
id: params.id,
|
|
80
80
|
parentId: params.parentId,
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
[QUEST_ACTIONS.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return runTool(questLog, toolCallId, { type: QUEST_ACTIONS.revert });
|
|
91
|
-
},
|
|
81
|
+
}),
|
|
82
|
+
[QUEST_ACTIONS.rules]: (questLog, _params, toolCallId) =>
|
|
83
|
+
runTool(questLog, toolCallId, { type: QUEST_ACTIONS.rules }),
|
|
84
|
+
[QUEST_ACTIONS.skill]: (questLog, _params, toolCallId) =>
|
|
85
|
+
runTool(questLog, toolCallId, { type: QUEST_ACTIONS.skill }),
|
|
86
|
+
[QUEST_ACTIONS.undo]: (questLog, _params, toolCallId) =>
|
|
87
|
+
runTool(questLog, toolCallId, { type: QUEST_ACTIONS.undo }),
|
|
88
|
+
[QUEST_ACTIONS.redo]: (questLog, _params, toolCallId) =>
|
|
89
|
+
runTool(questLog, toolCallId, { type: QUEST_ACTIONS.redo }),
|
|
92
90
|
};
|
|
93
91
|
|
|
94
92
|
function runTool(
|
|
@@ -104,11 +102,10 @@ function runTool(
|
|
|
104
102
|
action.type as (typeof SPLIT_DISPLAY_ACTIONS)[number],
|
|
105
103
|
)
|
|
106
104
|
? questLog.getAll()
|
|
107
|
-
: result.quest
|
|
108
|
-
|
|
109
|
-
: undefined;
|
|
105
|
+
: (result.quests ?? (result.quest ? [result.quest] : []));
|
|
106
|
+
const snapshotQuests = action.type === QUEST_ACTIONS.delete ? displayQuests : undefined;
|
|
110
107
|
|
|
111
|
-
return makeToolResult(result.message, questLog, displayQuests);
|
|
108
|
+
return makeToolResult(result.message, questLog, displayQuests, snapshotQuests);
|
|
112
109
|
}
|
|
113
110
|
|
|
114
111
|
export async function questToolExecute(
|
|
@@ -116,8 +113,13 @@ export async function questToolExecute(
|
|
|
116
113
|
toolCallId: string,
|
|
117
114
|
params: QuestToolParams,
|
|
118
115
|
): Promise<AgentToolResult<unknown>> {
|
|
119
|
-
logger.debug("quests:tool", "execute", {
|
|
120
|
-
|
|
116
|
+
logger.debug("quests:tool", "execute", {
|
|
117
|
+
toolCallId,
|
|
118
|
+
action: params.action,
|
|
119
|
+
id: params.id,
|
|
120
|
+
ids: params.ids,
|
|
121
|
+
});
|
|
122
|
+
const handler = toolHandlers[params.action as QuestActionType];
|
|
121
123
|
return handler(questLog, params, toolCallId);
|
|
122
124
|
}
|
|
123
125
|
|
|
@@ -132,15 +134,43 @@ export function registerQuestTool(
|
|
|
132
134
|
label: "Quest",
|
|
133
135
|
description:
|
|
134
136
|
"Manage the session quest log and retrieve the complete quest system documentation. " +
|
|
135
|
-
"Use this VERY frequently to track tasks, plans, and progress. " +
|
|
136
137
|
"When you need to understand quests, steps, rules, or best practices, use action: 'skill' or action: 'rules'.",
|
|
137
138
|
promptSnippet:
|
|
138
|
-
"Manage quests and steps, or retrieve quest rules and best practices via skill/rules"
|
|
139
|
+
"Manage quests and steps, or retrieve quest rules and best practices via skill/rules. " +
|
|
140
|
+
"Use this VERY frequently to track tasks, plans, and progress. ",
|
|
139
141
|
promptGuidelines: [QUEST_PROMPT_GATE, ...QUEST_PROMPT_REMINDER],
|
|
140
142
|
parameters: createQuestParams(config.ids.length),
|
|
141
143
|
execute: (toolCallId, params, _signal, _onUpdate, _ctx) =>
|
|
142
144
|
questToolExecute(questLog, toolCallId, params),
|
|
143
|
-
renderCall: renderQuestCall
|
|
145
|
+
renderCall: renderQuestCall as unknown as (
|
|
146
|
+
args: object,
|
|
147
|
+
theme: Theme,
|
|
148
|
+
context: unknown,
|
|
149
|
+
) => ReturnType<typeof renderQuestCall>,
|
|
150
|
+
renderResult: renderQuestResult(config),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
pi.registerTool({
|
|
154
|
+
name: "learn_quests",
|
|
155
|
+
label: "Learn Quests",
|
|
156
|
+
description: "Learn about the quest system and how to use it effectively.",
|
|
157
|
+
promptSnippet: "Use this tool to learn about the quest system and how to use it effectively.",
|
|
158
|
+
promptGuidelines: [
|
|
159
|
+
"Use this tool when on a session to understand how and when to use quests.",
|
|
160
|
+
"ALWAYS run this tool when on a new session before starting any work.",
|
|
161
|
+
],
|
|
162
|
+
parameters: Type.Object(
|
|
163
|
+
{},
|
|
164
|
+
{ description: "No parameters required for learning about quests" },
|
|
165
|
+
),
|
|
166
|
+
execute: async (_toolCallId, _params, _signal, _onUpdate, _ctx) =>
|
|
167
|
+
questToolExecute(questLog, "learn_quests", { action: QUEST_ACTIONS.skill }),
|
|
168
|
+
renderCall: ((_args: object, theme: Theme, context: unknown) =>
|
|
169
|
+
renderQuestCall({ action: QUEST_ACTIONS.skill }, theme, context)) as unknown as (
|
|
170
|
+
args: object,
|
|
171
|
+
theme: Theme,
|
|
172
|
+
context: unknown,
|
|
173
|
+
) => ReturnType<typeof renderQuestCall>,
|
|
144
174
|
renderResult: renderQuestResult(config),
|
|
145
175
|
});
|
|
146
176
|
}
|
package/src/tools/params.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StringEnum } from "@mariozechner/pi-ai";
|
|
2
|
-
import { Type } from "
|
|
2
|
+
import { Type } from "typebox";
|
|
3
3
|
import { QUEST_ACTION_VALUES } from "../quest/types.js";
|
|
4
4
|
|
|
5
5
|
export function createQuestParams(idLength: number) {
|
|
@@ -22,7 +22,13 @@ export function createQuestParams(idLength: number) {
|
|
|
22
22
|
id: Type.Optional(
|
|
23
23
|
Type.String({
|
|
24
24
|
pattern,
|
|
25
|
-
description: `Quest ID (required for
|
|
25
|
+
description: `Quest ID (required for update, reorder, and reparent actions. For toggle and delete, provide either id or ids. ALWAYS use the ${idLength}-digit hex ID shown in brackets, never the positional number.`,
|
|
26
|
+
}),
|
|
27
|
+
),
|
|
28
|
+
ids: Type.Optional(
|
|
29
|
+
Type.Array(Type.String({ pattern }), {
|
|
30
|
+
minItems: 1,
|
|
31
|
+
description: `Quest IDs for batch toggle or delete actions. Use this when operating on more than one ${idLength}-digit hex quest ID at once.`,
|
|
26
32
|
}),
|
|
27
33
|
),
|
|
28
34
|
targetId: Type.Optional(
|