@rmrdeveloper/sideroom-pi 7.0.0 → 7.0.1
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/extensions/ask/index.ts +7 -1
- package/extensions/ask/model.ts +109 -4
- package/extensions/todo/index.ts +2 -1
- package/extensions/todo/model.ts +75 -4
- package/package.json +1 -1
package/extensions/ask/index.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
2
|
import { Text } from '@earendil-works/pi-tui';
|
|
3
3
|
import { executeAsk } from './execute.ts';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AskParamsSchema,
|
|
6
|
+
type AskResult,
|
|
7
|
+
prepareAskArguments,
|
|
8
|
+
TOOL_NAME,
|
|
9
|
+
} from './model.ts';
|
|
5
10
|
|
|
6
11
|
export const ASK_DESCRIPTION =
|
|
7
12
|
'Ask the user one or more questions in the language they are speaking. Use for clarifying requirements, getting preferences, or confirming decisions. Each question must include a recommended option. For a single question, shows a simple option list. For multiple questions, shows a tab-based interface.';
|
|
@@ -27,6 +32,7 @@ export default function registerAsk(pi: ExtensionAPI): void {
|
|
|
27
32
|
promptSnippet: ASK_PROMPT_SNIPPET,
|
|
28
33
|
promptGuidelines: ASK_PROMPT_GUIDELINES,
|
|
29
34
|
parameters: AskParamsSchema,
|
|
35
|
+
prepareArguments: prepareAskArguments,
|
|
30
36
|
executionMode: 'sequential',
|
|
31
37
|
execute: async (_toolCallId, params, _signal, _onUpdate, ctx) =>
|
|
32
38
|
executeAsk(params, ctx),
|
package/extensions/ask/model.ts
CHANGED
|
@@ -79,13 +79,118 @@ export type ParseAskResult =
|
|
|
79
79
|
| { readonly ok: true; readonly questions: readonly AskQuestion[] }
|
|
80
80
|
| { readonly ok: false; readonly message: string };
|
|
81
81
|
|
|
82
|
+
interface JsonWireObject {
|
|
83
|
+
readonly [key: string]: JsonWireValue | undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
type JsonWireValue =
|
|
87
|
+
| string
|
|
88
|
+
| number
|
|
89
|
+
| boolean
|
|
90
|
+
| null
|
|
91
|
+
| readonly JsonWireValue[]
|
|
92
|
+
| JsonWireObject;
|
|
93
|
+
|
|
94
|
+
export function prepareAskArguments(args: unknown): AskParams {
|
|
95
|
+
return decodeAskWireArgs(args) as AskParams;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function decodeAskWireArgs(args: unknown): JsonWireValue {
|
|
99
|
+
if (!isJsonWireObject(args)) {
|
|
100
|
+
return toJsonWireValue(args);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const questions = decodeAskQuestions(args.questions);
|
|
104
|
+
if (questions === args.questions) {
|
|
105
|
+
return args;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { ...args, questions };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function decodeAskQuestions(
|
|
112
|
+
value: JsonWireValue | undefined,
|
|
113
|
+
): JsonWireValue | undefined {
|
|
114
|
+
if (value === undefined) {
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const parsed = decodeJsonWireField(value);
|
|
119
|
+
if (!Array.isArray(parsed)) {
|
|
120
|
+
return parsed;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let changed = parsed !== value;
|
|
124
|
+
const questions = parsed.map((question) => {
|
|
125
|
+
const decoded = decodeAskQuestion(question);
|
|
126
|
+
if (decoded !== question) {
|
|
127
|
+
changed = true;
|
|
128
|
+
}
|
|
129
|
+
return decoded;
|
|
130
|
+
});
|
|
131
|
+
if (!changed) {
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
return questions;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function decodeAskQuestion(question: JsonWireValue): JsonWireValue {
|
|
138
|
+
if (!isJsonWireObject(question)) {
|
|
139
|
+
return question;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const options = decodeJsonWireField(question.options);
|
|
143
|
+
if (options === question.options) {
|
|
144
|
+
return question;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return { ...question, options };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function decodeJsonWireField(
|
|
151
|
+
value: JsonWireValue | undefined,
|
|
152
|
+
): JsonWireValue | undefined {
|
|
153
|
+
if (typeof value !== 'string') {
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
return JSON.parse(value) as JsonWireValue;
|
|
159
|
+
} catch {
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function isJsonWireObject(value: unknown): value is JsonWireObject {
|
|
165
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function toJsonWireValue(value: unknown): JsonWireValue {
|
|
169
|
+
if (
|
|
170
|
+
value === null ||
|
|
171
|
+
typeof value === 'string' ||
|
|
172
|
+
typeof value === 'number' ||
|
|
173
|
+
typeof value === 'boolean'
|
|
174
|
+
) {
|
|
175
|
+
return value;
|
|
176
|
+
}
|
|
177
|
+
if (Array.isArray(value)) {
|
|
178
|
+
return value.map(toJsonWireValue);
|
|
179
|
+
}
|
|
180
|
+
if (isJsonWireObject(value)) {
|
|
181
|
+
return value;
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
82
186
|
export function parseAskParams(value: unknown): ParseAskResult {
|
|
83
|
-
|
|
84
|
-
|
|
187
|
+
const decoded = decodeAskWireArgs(value);
|
|
188
|
+
if (!Check(AskParamsSchema, decoded)) {
|
|
189
|
+
return { ok: false, message: schemaErrorMessage(decoded) };
|
|
85
190
|
}
|
|
86
191
|
|
|
87
192
|
const seen = new Set<string>();
|
|
88
|
-
for (const question of
|
|
193
|
+
for (const question of decoded.questions) {
|
|
89
194
|
if (seen.has(question.id)) {
|
|
90
195
|
return {
|
|
91
196
|
ok: false,
|
|
@@ -101,7 +206,7 @@ export function parseAskParams(value: unknown): ParseAskResult {
|
|
|
101
206
|
}
|
|
102
207
|
}
|
|
103
208
|
|
|
104
|
-
return { ok: true, questions: normalizeQuestions(
|
|
209
|
+
return { ok: true, questions: normalizeQuestions(decoded.questions) };
|
|
105
210
|
}
|
|
106
211
|
|
|
107
212
|
export function normalizeQuestions(
|
package/extensions/todo/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
|
2
2
|
import { Text } from '@earendil-works/pi-tui';
|
|
3
3
|
import { executeTodo, type TodoToolDetails } from './execute.ts';
|
|
4
4
|
import { registerGuardEvents } from './guards.ts';
|
|
5
|
-
import { TOOL_NAME, TodoParamsSchema } from './model.ts';
|
|
5
|
+
import { prepareTodoArguments, TOOL_NAME, TodoParamsSchema } from './model.ts';
|
|
6
6
|
import {
|
|
7
7
|
type BoardLifecycle,
|
|
8
8
|
commitBoard,
|
|
@@ -60,6 +60,7 @@ function registerTodoTool(
|
|
|
60
60
|
promptSnippet: TODO_PROMPT_SNIPPET,
|
|
61
61
|
promptGuidelines: TODO_PROMPT_GUIDELINES,
|
|
62
62
|
parameters: TodoParamsSchema,
|
|
63
|
+
prepareArguments: prepareTodoArguments,
|
|
63
64
|
executionMode: 'sequential',
|
|
64
65
|
execute: async (_toolCallId, params, _signal, _onUpdate, ctx) => {
|
|
65
66
|
const result = executeTodo(params, ctx, store.items);
|
package/extensions/todo/model.ts
CHANGED
|
@@ -115,13 +115,84 @@ const STATUS_MARKS: Readonly<Record<TodoStatus, string>> = {
|
|
|
115
115
|
const BOARD_INSTRUCTIONS =
|
|
116
116
|
'Call sideroom_todo update before the next item. While any item is pending, exactly one must be in_progress. Complete the current item and start the next in the same update. propose replaces the list; update patches by id.';
|
|
117
117
|
|
|
118
|
+
interface JsonWireObject {
|
|
119
|
+
readonly [key: string]: JsonWireValue | undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
type JsonWireValue =
|
|
123
|
+
| string
|
|
124
|
+
| number
|
|
125
|
+
| boolean
|
|
126
|
+
| null
|
|
127
|
+
| readonly JsonWireValue[]
|
|
128
|
+
| JsonWireObject;
|
|
129
|
+
|
|
130
|
+
export function prepareTodoArguments(args: unknown): TodoParams {
|
|
131
|
+
return decodeTodoWireArgs(args) as TodoParams;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function decodeTodoWireArgs(args: unknown): JsonWireValue {
|
|
135
|
+
if (!isJsonWireObject(args)) {
|
|
136
|
+
return toJsonWireValue(args);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const items = decodeJsonWireField(args.items);
|
|
140
|
+
const patches = decodeJsonWireField(args.patches);
|
|
141
|
+
if (items === args.items && patches === args.patches) {
|
|
142
|
+
return args;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
...args,
|
|
147
|
+
...('items' in args ? { items } : {}),
|
|
148
|
+
...('patches' in args ? { patches } : {}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function decodeJsonWireField(
|
|
153
|
+
value: JsonWireValue | undefined,
|
|
154
|
+
): JsonWireValue | undefined {
|
|
155
|
+
if (typeof value !== 'string') {
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(value) as JsonWireValue;
|
|
161
|
+
} catch {
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function isJsonWireObject(value: unknown): value is JsonWireObject {
|
|
167
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function toJsonWireValue(value: unknown): JsonWireValue {
|
|
171
|
+
if (
|
|
172
|
+
value === null ||
|
|
173
|
+
typeof value === 'string' ||
|
|
174
|
+
typeof value === 'number' ||
|
|
175
|
+
typeof value === 'boolean'
|
|
176
|
+
) {
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
if (Array.isArray(value)) {
|
|
180
|
+
return value.map(toJsonWireValue);
|
|
181
|
+
}
|
|
182
|
+
if (isJsonWireObject(value)) {
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
118
188
|
export function parseTodoParams(value: unknown): ParseTodoResult {
|
|
119
|
-
|
|
189
|
+
const decoded = decodeTodoWireArgs(value);
|
|
190
|
+
if (!Check(TodoParamsSchema, decoded)) {
|
|
120
191
|
return { ok: false, message: 'Error: Invalid sideroom_todo parameters' };
|
|
121
192
|
}
|
|
122
193
|
|
|
123
|
-
if (
|
|
124
|
-
const items = normalizeItems(
|
|
194
|
+
if (decoded.action === TODO_ACTION.propose) {
|
|
195
|
+
const items = normalizeItems(decoded.items);
|
|
125
196
|
if (!items.ok) {
|
|
126
197
|
return { ok: false, message: items.message };
|
|
127
198
|
}
|
|
@@ -135,7 +206,7 @@ export function parseTodoParams(value: unknown): ParseTodoResult {
|
|
|
135
206
|
};
|
|
136
207
|
}
|
|
137
208
|
|
|
138
|
-
const patches = normalizePatches(
|
|
209
|
+
const patches = normalizePatches(decoded.patches);
|
|
139
210
|
if (!patches.ok) {
|
|
140
211
|
return { ok: false, message: patches.message };
|
|
141
212
|
}
|
package/package.json
CHANGED