pi-ask-user-questions 0.1.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/LICENSE +21 -0
- package/README.md +127 -0
- package/extensions/ask-user.ts +243 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 qunm00
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# pi-ask-user-questions
|
|
2
|
+
|
|
3
|
+
**The agent asks a question. You type the answer. There is no list to pick from.**
|
|
4
|
+
|
|
5
|
+
If you want the agent to *offer you options* — searchable lists, multi-select, split-pane
|
|
6
|
+
previews, overlay modes — use [`pi-ask-user`](https://www.npmjs.com/package/pi-ask-user)
|
|
7
|
+
instead. It is further along and does more. This package is for the other case: you want
|
|
8
|
+
to be **asked in words and answer in words**, and you want to be asked **less**.
|
|
9
|
+
|
|
10
|
+
## How it differs
|
|
11
|
+
|
|
12
|
+
| | `pi-ask-user-questions` (this) | `pi-ask-user` |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| Answer format | free-form text, always | option list, with freeform as an extra |
|
|
15
|
+
| Interaction | one question, one text field | menus, multi-select, split-pane preview, configurable overlay/inline |
|
|
16
|
+
| Effect on interruptions | guidelines push the agent to ask **less** | bundles a skill that **mandates** asking at decision gates |
|
|
17
|
+
| Maturity | new | 25 published versions, actively maintained |
|
|
18
|
+
|
|
19
|
+
That last row is the real trade. This package is deliberately narrower: fewer moving parts,
|
|
20
|
+
and one abort-safe input path instead of a configurable TUI. If you want richer selection
|
|
21
|
+
UI, the other package is the better tool and there is no reason to fight it.
|
|
22
|
+
|
|
23
|
+
## The anti-pester stance
|
|
24
|
+
|
|
25
|
+
The usual failure mode is an agent that asks about everything. Three things push back,
|
|
26
|
+
all in the tool's `promptGuidelines` — which Pi appends to your system prompt while the
|
|
27
|
+
tool is active:
|
|
28
|
+
|
|
29
|
+
- **Batch everything into one call.** The agent collects all its questions up front, so you
|
|
30
|
+
answer in a single pass instead of being interrupted repeatedly.
|
|
31
|
+
- **Don't ask what you can work out.** Reading the repo, inferring, or choosing a sensible
|
|
32
|
+
default is preferred over asking. State the assumption and move on.
|
|
33
|
+
- **Never invent an answer.** Skipped questions come back marked as skips, with an explicit
|
|
34
|
+
instruction not to fill in the blank. A skip means "no answer", not "assume something".
|
|
35
|
+
|
|
36
|
+
[`pi-ask-user`](https://www.npmjs.com/package/pi-ask-user) takes the opposite position: it
|
|
37
|
+
ships a skill that *requires* the agent to stop and ask at high-stakes decision points. Both
|
|
38
|
+
are defensible. If being interrupted too much is your problem, this is the one you want.
|
|
39
|
+
|
|
40
|
+
The exact guidelines, verbatim:
|
|
41
|
+
|
|
42
|
+
> - Batch every question you need into a single `ask_user` call; the user answers them in sequence.
|
|
43
|
+
> - Ask only when you truly cannot proceed. Never ask what you can read from the repo, infer, or decide yourself.
|
|
44
|
+
> - State an assumption and continue instead of asking.
|
|
45
|
+
> - Phrase each question to be answerable in one typed sentence — the user types text, they do not pick from options.
|
|
46
|
+
> - A skipped question means the user declined to answer. Never fabricate an answer; re-ask or proceed without it.
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# from npm
|
|
52
|
+
pi install npm:pi-ask-user-questions
|
|
53
|
+
|
|
54
|
+
# from git
|
|
55
|
+
pi install git:github.com/qunm00/pi-ask-user-questions@v1
|
|
56
|
+
|
|
57
|
+
# try it for one run without saving
|
|
58
|
+
pi -e npm:pi-ask-user-questions
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Then enable the tool in a session if it is not already active:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
/tools
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## How it behaves
|
|
68
|
+
|
|
69
|
+
- **One dialog at a time**, titled `Question 2 of 3: <the question>`. Questions are asked
|
|
70
|
+
and answered in the order given.
|
|
71
|
+
- **Enter** submits. **Escape** skips that one question and moves to the next — you keep
|
|
72
|
+
your momentum instead of restarting the batch.
|
|
73
|
+
- Submitting an empty answer re-prompts once, because a stray Enter is usually an accident.
|
|
74
|
+
Press Escape at the re-prompt to skip.
|
|
75
|
+
- Skipped questions are reported to the model explicitly, with an instruction not to invent
|
|
76
|
+
an answer.
|
|
77
|
+
- Batching holds even when the agent is careless: the tool is registered with
|
|
78
|
+
`executionMode: "sequential"`, so three separate `ask_user` calls in one assistant message
|
|
79
|
+
queue instead of stacking.
|
|
80
|
+
|
|
81
|
+
## Limitations
|
|
82
|
+
|
|
83
|
+
These are deliberate, not oversights:
|
|
84
|
+
|
|
85
|
+
- **Single-line answers.** `ui.editor()` accepts no `AbortSignal`, so aborting a turn while
|
|
86
|
+
a multiline dialog is open would leave the dialog waiting and the turn unable to finish.
|
|
87
|
+
Single-line input is abort-safe. Pasted multi-line text is preserved, but you cannot type
|
|
88
|
+
a newline mid-answer.
|
|
89
|
+
- **No `placeholder` support.** In pi 0.87.1 the `ui.input` placeholder argument is taken by
|
|
90
|
+
`ExtensionInputComponent` as `_placeholder` and never read. There is nowhere to put an
|
|
91
|
+
example answer, so put any needed guidance in the question text.
|
|
92
|
+
- **No timeout.** If the agent asks and you walk away, the dialog waits. Press Escape to skip.
|
|
93
|
+
- **Needs an interactive session.** In `print`/`json` mode there is no UI; the tool returns
|
|
94
|
+
a message telling the model to ask in plain text instead of failing the turn.
|
|
95
|
+
|
|
96
|
+
## Development
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm install
|
|
100
|
+
npm test # 19 tests, includes a real npm pack + install + load check
|
|
101
|
+
npm run test:unit # skips the slow sandbox install check
|
|
102
|
+
npm run typecheck # tsc --noEmit
|
|
103
|
+
|
|
104
|
+
# load it into a live pi without installing
|
|
105
|
+
pi -e ./extensions/ask-user.ts
|
|
106
|
+
|
|
107
|
+
# then, in that session:
|
|
108
|
+
/ask-demo
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`/ask-demo` exercises the real dialog path without involving a model, so you can check
|
|
112
|
+
rendering and the skip/empty behaviour by hand.
|
|
113
|
+
|
|
114
|
+
Tests run on [`@marcfargas/pi-test-harness`](https://github.com/marcfargas/pi-test-harness),
|
|
115
|
+
which keeps pi real — real extension loading, real hooks, real tool registry — and
|
|
116
|
+
substitutes only the model boundary (`streamFn`) and `ctx.ui.*`.
|
|
117
|
+
|
|
118
|
+
That harness targets pi 0.75.x. Three things moved by 0.87 and are bridged in
|
|
119
|
+
`__tests__/support/pi-compat.ts` and `vitest.config.ts`; the comments there explain each
|
|
120
|
+
one. Remove those shims once a harness release supports 0.87.
|
|
121
|
+
|
|
122
|
+
Because the harness substitutes `ctx.ui.*`, **no automated test can check terminal
|
|
123
|
+
rendering**. `/ask-demo` is the manual gate for that.
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ask_user — ask the user a question, get a typed free-form answer.
|
|
3
|
+
*
|
|
4
|
+
* The agent calls this tool; the user answers by typing. There are no options
|
|
5
|
+
* to pick from. When several questions are needed, the user answers them one
|
|
6
|
+
* after another in a single run.
|
|
7
|
+
*
|
|
8
|
+
* Design notes worth keeping:
|
|
9
|
+
*
|
|
10
|
+
* - `executionMode: "sequential"` is load-bearing. Tool calls from one
|
|
11
|
+
* assistant message can otherwise run in parallel, and pi renders dialogs
|
|
12
|
+
* into a single shared editor container — two concurrent dialogs collide.
|
|
13
|
+
* Sequential also means that when the agent asks three questions as three
|
|
14
|
+
* separate calls, they queue instead of stacking.
|
|
15
|
+
*
|
|
16
|
+
* - The dialog title is the ONLY channel that renders. In pi 0.87.1
|
|
17
|
+
* `ui.input`'s second argument is taken by `ExtensionInputComponent` as
|
|
18
|
+
* `_placeholder` and never read, so it is a silent no-op. We therefore put
|
|
19
|
+
* the question, the progress counter and any hint all in the title, and
|
|
20
|
+
* deliberately expose no `placeholder` parameter to the model.
|
|
21
|
+
*
|
|
22
|
+
* - `ui.editor()` (multiline) is intentionally NOT used. Unlike `ui.input`,
|
|
23
|
+
* it accepts no AbortSignal, so aborting a turn while a multiline dialog is
|
|
24
|
+
* open leaves the dialog waiting and the turn unable to finish. Single-line
|
|
25
|
+
* input is abort-safe. Multiline is a possible follow-up once that path
|
|
26
|
+
* can be made cancellable.
|
|
27
|
+
*
|
|
28
|
+
* - `promptGuidelines` do the real work of stopping the agent pestering the
|
|
29
|
+
* user: batch into one call, don't ask what you can determine yourself, and
|
|
30
|
+
* never invent an answer.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { Type } from "typebox";
|
|
34
|
+
import { defineTool, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
35
|
+
|
|
36
|
+
interface AskQuestion {
|
|
37
|
+
question: string;
|
|
38
|
+
hint?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface AskAnswer {
|
|
42
|
+
question: string;
|
|
43
|
+
hint?: string;
|
|
44
|
+
answer: string;
|
|
45
|
+
skipped: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface AskDetails {
|
|
49
|
+
answers: AskAnswer[];
|
|
50
|
+
/** True when an abort signal stopped the batch before every question was shown. */
|
|
51
|
+
aborted: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const MAX_QUESTIONS = 10;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Build the dialog title. This is the only text the user actually sees, so it
|
|
58
|
+
* carries the counter, the question and the hint. `Text` wraps, so length is
|
|
59
|
+
* not a problem.
|
|
60
|
+
*/
|
|
61
|
+
function formatTitle(index: number, total: number, question: AskQuestion, note?: string): string {
|
|
62
|
+
const position = `Question ${index + 1} of ${total}`;
|
|
63
|
+
const prefix = note ? `${position} — ${note}` : position;
|
|
64
|
+
return `${prefix}: ${question.question}${question.hint ? ` (${question.hint})` : ""}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Ask each question in order, one dialog at a time. Never returns until every
|
|
69
|
+
* question is answered, skipped, or the turn is aborted.
|
|
70
|
+
*/
|
|
71
|
+
async function askQuestions(
|
|
72
|
+
ctx: ExtensionContext,
|
|
73
|
+
questions: AskQuestion[],
|
|
74
|
+
signal: AbortSignal | undefined,
|
|
75
|
+
): Promise<AskDetails> {
|
|
76
|
+
const answers: AskAnswer[] = [];
|
|
77
|
+
let aborted = false;
|
|
78
|
+
|
|
79
|
+
for (const [index, question] of questions.entries()) {
|
|
80
|
+
if (signal?.aborted) {
|
|
81
|
+
aborted = true;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let answer = await ctx.ui.input(formatTitle(index, questions.length, question), undefined, { signal });
|
|
86
|
+
|
|
87
|
+
if (signal?.aborted) {
|
|
88
|
+
aborted = true;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// An accidental Enter is likelier than a deliberate blank answer, so
|
|
93
|
+
// confirm once before recording it as a skip. Escape skips immediately.
|
|
94
|
+
if (answer !== undefined && answer.trim() === "") {
|
|
95
|
+
answer = await ctx.ui.input(
|
|
96
|
+
formatTitle(index, questions.length, question, "empty, Escape to skip"),
|
|
97
|
+
undefined,
|
|
98
|
+
{ signal },
|
|
99
|
+
);
|
|
100
|
+
if (signal?.aborted) {
|
|
101
|
+
aborted = true;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const text = answer ?? "";
|
|
107
|
+
const skipped = text.trim() === "";
|
|
108
|
+
answers.push({
|
|
109
|
+
question: question.question,
|
|
110
|
+
hint: question.hint,
|
|
111
|
+
answer: skipped ? "" : text,
|
|
112
|
+
skipped,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { answers, aborted };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Model-facing transcript. Explicit about skips so nothing gets invented. */
|
|
120
|
+
function buildContent(details: AskDetails, requested: number): string {
|
|
121
|
+
const lines: string[] = [];
|
|
122
|
+
|
|
123
|
+
for (const [index, entry] of details.answers.entries()) {
|
|
124
|
+
lines.push(`Q${index + 1}: ${entry.question}`);
|
|
125
|
+
lines.push(`A${index + 1}: ${entry.skipped ? "(skipped — the user did not answer)" : entry.answer}`);
|
|
126
|
+
lines.push("");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (details.answers.length === 0) {
|
|
130
|
+
lines.push("The user did not answer any question.");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const skipped = details.answers.filter((entry) => entry.skipped).length;
|
|
134
|
+
if (skipped > 0) {
|
|
135
|
+
lines.push(
|
|
136
|
+
`${skipped} of ${requested} question(s) went unanswered. Do not fabricate an answer — ` +
|
|
137
|
+
`either re-ask ${skipped === 1 ? "it" : "them"} or proceed without ${skipped === 1 ? "it" : "them"}.`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const neverShown = requested - details.answers.length;
|
|
142
|
+
if (details.aborted) {
|
|
143
|
+
lines.push(
|
|
144
|
+
`The turn was interrupted before ${neverShown} question(s) were shown. ` +
|
|
145
|
+
"Do not assume answers to questions that were never asked.",
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return lines.join("\n").trimEnd();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const askUserTool = defineTool({
|
|
153
|
+
name: "ask_user",
|
|
154
|
+
label: "Ask User",
|
|
155
|
+
description:
|
|
156
|
+
"Ask the user a question and read back the answer they type. The user types free-form text — " +
|
|
157
|
+
"there is no list of options to choose from, so phrase each question so it can be answered in " +
|
|
158
|
+
"a sentence. Put every question you need into a single call; the user answers them in sequence. " +
|
|
159
|
+
"Use this only when you genuinely cannot proceed: never ask what you can read from the repo, " +
|
|
160
|
+
"infer, or decide yourself. If a question comes back skipped, the user declined to answer it — " +
|
|
161
|
+
"never invent an answer for it.",
|
|
162
|
+
promptSnippet: "Ask the user a question and read their typed free-form answer",
|
|
163
|
+
promptGuidelines: [
|
|
164
|
+
"Batch every question you need into a single ask_user call; the user answers them in sequence.",
|
|
165
|
+
"Ask only when you truly cannot proceed. Never ask what you can read from the repo, infer, or decide yourself.",
|
|
166
|
+
"State an assumption and continue instead of asking.",
|
|
167
|
+
"Phrase each question to be answerable in one typed sentence — the user types text, they do not pick from options.",
|
|
168
|
+
"A skipped question means the user declined to answer. Never fabricate an answer; re-ask or proceed without it.",
|
|
169
|
+
],
|
|
170
|
+
parameters: Type.Object({
|
|
171
|
+
questions: Type.Array(
|
|
172
|
+
Type.Object({
|
|
173
|
+
question: Type.String({
|
|
174
|
+
description: "The question to ask, phrased so it can be answered in one typed sentence.",
|
|
175
|
+
}),
|
|
176
|
+
hint: Type.Optional(
|
|
177
|
+
Type.String({
|
|
178
|
+
description: "Optional short context for why you are asking, shown with the question.",
|
|
179
|
+
}),
|
|
180
|
+
),
|
|
181
|
+
}),
|
|
182
|
+
{
|
|
183
|
+
minItems: 1,
|
|
184
|
+
maxItems: MAX_QUESTIONS,
|
|
185
|
+
description: "Every question you need answered, asked and answered in this order.",
|
|
186
|
+
},
|
|
187
|
+
),
|
|
188
|
+
}),
|
|
189
|
+
executionMode: "sequential",
|
|
190
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
191
|
+
if (!ctx.hasUI) {
|
|
192
|
+
return {
|
|
193
|
+
content: [
|
|
194
|
+
{
|
|
195
|
+
type: "text",
|
|
196
|
+
text:
|
|
197
|
+
"ask_user needs an interactive session and this run has no UI. " +
|
|
198
|
+
"Ask the user directly in plain text instead of calling this tool.",
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
details: { answers: [], aborted: false } as AskDetails,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const details = await askQuestions(ctx, params.questions, signal);
|
|
206
|
+
return {
|
|
207
|
+
content: [{ type: "text", text: buildContent(details, params.questions.length) }],
|
|
208
|
+
details,
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
/** Questions used by /ask-demo to exercise the dialogs without a model. */
|
|
214
|
+
const DEMO_QUESTIONS: AskQuestion[] = [
|
|
215
|
+
{ question: "What should I call you?", hint: "used in the greeting" },
|
|
216
|
+
{ question: "What are we working on today?" },
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
export default function (pi: ExtensionAPI) {
|
|
220
|
+
pi.registerTool(askUserTool);
|
|
221
|
+
|
|
222
|
+
pi.registerCommand("ask-demo", {
|
|
223
|
+
description: "Manually test the ask dialogs (same code path as ask_user)",
|
|
224
|
+
handler: async (_args, ctx) => {
|
|
225
|
+
ctx.ui.notify(`/ask-demo invoked (mode=${ctx.mode}, hasUI=${ctx.hasUI})`, "info");
|
|
226
|
+
|
|
227
|
+
if (!ctx.hasUI) {
|
|
228
|
+
ctx.ui.notify("ask-demo needs an interactive session. Drop --mode/--print.", "error");
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const details = await askQuestions(ctx, DEMO_QUESTIONS, ctx.signal);
|
|
233
|
+
|
|
234
|
+
for (const [index, entry] of details.answers.entries()) {
|
|
235
|
+
ctx.ui.notify(
|
|
236
|
+
`Q${index + 1}: ${entry.skipped ? "skipped (Escape)" : entry.answer}`,
|
|
237
|
+
entry.skipped ? "warning" : "info",
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
if (details.aborted) ctx.ui.notify("aborted before all questions were shown", "warning");
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-ask-user-questions",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Let the agent ask you a question and type your answer. No multiple choice. Batch questions are answered consecutively.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"pi-package",
|
|
9
|
+
"pi",
|
|
10
|
+
"pi-extension"
|
|
11
|
+
],
|
|
12
|
+
"files": [
|
|
13
|
+
"extensions/**/*.ts",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"pi": {
|
|
18
|
+
"extensions": [
|
|
19
|
+
"./extensions/ask-user.ts"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:unit": "vitest run __tests__/ask-user.test.ts __tests__/mock-ui.test.ts __tests__/smoke.test.ts",
|
|
25
|
+
"typecheck": "tsc --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@earendil-works/pi-agent-core": "*",
|
|
29
|
+
"@earendil-works/pi-ai": "*",
|
|
30
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
31
|
+
"typebox": "*"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@earendil-works/pi-agent-core": "0.87.1",
|
|
35
|
+
"@earendil-works/pi-ai": "0.87.1",
|
|
36
|
+
"@earendil-works/pi-coding-agent": "0.87.1",
|
|
37
|
+
"@marcfargas/pi-test-harness": "0.6.1",
|
|
38
|
+
"@types/node": "^26.6.3",
|
|
39
|
+
"typebox": "^1.1.38",
|
|
40
|
+
"typescript": "^5.8.3",
|
|
41
|
+
"vitest": "^3.1.4"
|
|
42
|
+
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+github.com/qunm00/pi-ask-user-questions.git"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/qunm00/pi-ask-user-questions#readme",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/qunm00/pi-ask-user-questions/issues"
|
|
50
|
+
}
|
|
51
|
+
}
|