@fadhelhaidar/pi-ask-user 0.0.0-stage → 1.0.2
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/NOTICE.md +43 -0
- package/README.md +111 -2
- package/dist/chunks/external-editor-5wd19nyg.js +51 -0
- package/dist/chunks/index-bx2tbz93.js +5 -0
- package/dist/chunks/index-x7fb685n.js +226 -0
- package/dist/chunks/questionnaire-session-gdq5zqtt.js +2190 -0
- package/dist/events.d.ts +53 -0
- package/dist/events.js +8 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +538 -0
- package/dist/locales/de.json +31 -0
- package/dist/locales/en.json +29 -0
- package/dist/locales/es.json +31 -0
- package/dist/locales/fr.json +31 -0
- package/dist/locales/pt-BR.json +31 -0
- package/dist/locales/pt.json +31 -0
- package/dist/locales/ru.json +31 -0
- package/dist/locales/uk.json +31 -0
- package/dist/locales/zh.json +33 -0
- package/docs/configuration.md +121 -0
- package/docs/hosts.md +97 -0
- package/docs/keyboard.md +115 -0
- package/docs/localization.md +87 -0
- package/docs/tool-schema.md +127 -0
- package/package.json +64 -4
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Tool schema
|
|
2
|
+
|
|
3
|
+
The complete programmatic surface of `ask_user_question`: what the model sends, what
|
|
4
|
+
validation rejects, what comes back, and the event other extensions can listen to.
|
|
5
|
+
|
|
6
|
+
## Parameters
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
ask_user_question({
|
|
10
|
+
questions: [
|
|
11
|
+
{
|
|
12
|
+
question: string, // full question text, ends with "?"
|
|
13
|
+
header: string, // chip label, max 16 chars
|
|
14
|
+
options: [
|
|
15
|
+
{
|
|
16
|
+
label: string, // 1-5 words, max 60 chars
|
|
17
|
+
description: string, // what the choice means / its trade-off
|
|
18
|
+
preview?: string, // markdown rendered next to the options
|
|
19
|
+
},
|
|
20
|
+
// … 2-4 options total
|
|
21
|
+
],
|
|
22
|
+
multiSelect?: boolean, // default false
|
|
23
|
+
},
|
|
24
|
+
// … 1-4 questions total
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Limits
|
|
30
|
+
|
|
31
|
+
| Field | Constraint | Enforced by |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `questions` | 1-4 entries | TypeBox schema + `validateQuestionnaire` |
|
|
34
|
+
| `questions[].header` | max 16 characters | TypeBox schema only |
|
|
35
|
+
| `questions[].options` | 2-4 entries | TypeBox schema (both bounds) + `validateQuestionnaire` (minimum only) |
|
|
36
|
+
| `options[].label` | max 60 characters | TypeBox schema only |
|
|
37
|
+
| `options[].preview` | single-select questions only | tool description (multi-select tabs render checkbox rows) |
|
|
38
|
+
|
|
39
|
+
The two `maxLength` limits are checked by the parameter schema before `execute` runs;
|
|
40
|
+
the runtime validator does not re-check them.
|
|
41
|
+
|
|
42
|
+
### Reserved option labels
|
|
43
|
+
|
|
44
|
+
Authoring any of `"Other"`, `"Type something."`, or `"Next"` as an option label is
|
|
45
|
+
rejected with `reserved_label`. The last two are the runtime sentinel rows the dialog
|
|
46
|
+
appends itself; `"Other"` is reserved because models are conditioned to reach for it.
|
|
47
|
+
Reservation is unconditional — a single-select question rejects `"Next"` even though
|
|
48
|
+
that row is never appended there.
|
|
49
|
+
|
|
50
|
+
## Validation errors
|
|
51
|
+
|
|
52
|
+
Every rejection returns `cancelled: true`, an empty `answers` array, and an `error`
|
|
53
|
+
code. The `content[0].text` string is written for the model, not for a log.
|
|
54
|
+
|
|
55
|
+
| `error` | Cause |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| `no_questions` | `questions` was empty |
|
|
58
|
+
| `too_many_questions` | more than 4 questions in one call |
|
|
59
|
+
| `duplicate_question` | two questions with identical text |
|
|
60
|
+
| `empty_options` | a question carried fewer than 2 options |
|
|
61
|
+
| `reserved_label` | an option used a reserved label |
|
|
62
|
+
| `duplicate_option_label` | two options in one question share a label |
|
|
63
|
+
| `no_ui` | the run has no UI (`ctx.hasUI === false`) |
|
|
64
|
+
| `no_custom_ui` | the host cannot render custom UI and exposes no `select`/`input` dialogs |
|
|
65
|
+
| `session_load_failed` | the dialog module failed to import (dependencies changed on disk mid-session) |
|
|
66
|
+
| `stale_module_cache` | the loader cached a broken module after an earlier failed import; needs a Pi restart |
|
|
67
|
+
|
|
68
|
+
`reserved_label` short-circuits before `duplicate_option_label`.
|
|
69
|
+
|
|
70
|
+
## Result
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
{
|
|
74
|
+
content: [{ type: "text", text: string }], // envelope prose, or the decline message
|
|
75
|
+
details: {
|
|
76
|
+
answers: Array<{
|
|
77
|
+
questionIndex: number,
|
|
78
|
+
question: string,
|
|
79
|
+
kind: "option" | "custom" | "multi",
|
|
80
|
+
answer: string | null, // option label, typed text, or null for multi
|
|
81
|
+
selected?: string[], // chosen labels, multi-select only
|
|
82
|
+
notes?: string, // free-text note, when you wrote one
|
|
83
|
+
preview?: string, // echoed back when the chosen option carried a preview
|
|
84
|
+
}>,
|
|
85
|
+
cancelled: boolean,
|
|
86
|
+
globalNote?: string, // Submit-tab note; present even when cancelled is true
|
|
87
|
+
error?: QuestionnaireError, // one of the codes above
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Envelope text
|
|
93
|
+
|
|
94
|
+
On success the text reads `User has answered your questions: "<question>"="<answer>". …
|
|
95
|
+
You can now continue with the user's answers in mind.` A chosen option's `preview` is
|
|
96
|
+
appended as `selected preview: <markdown>`, a per-question note as `user notes: <text>`,
|
|
97
|
+
and the Submit tab's global note as a trailing `global note: <text>` segment. A global
|
|
98
|
+
note alone still yields the answered envelope — it counts as an answer even when every
|
|
99
|
+
question is blank.
|
|
100
|
+
|
|
101
|
+
Cancelling, and any result with neither answer segments nor a global note, both collapse
|
|
102
|
+
to the single string `User declined to answer questions` so the model sees one canonical
|
|
103
|
+
signal. Partial submission is allowed: unanswered questions simply contribute no segment.
|
|
104
|
+
A cancelled result always reads as the decline in text; its note, if any, survives only
|
|
105
|
+
in `details.globalNote`.
|
|
106
|
+
|
|
107
|
+
## Event contract
|
|
108
|
+
|
|
109
|
+
The package publishes one event on Pi's event bus, emitted after validation passes and
|
|
110
|
+
before the dialog is shown. Import it from the `/events` subpath:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { ASK_USER_PROMPT_EVENT, type AskUserPromptEventPayload } from "@juicesharp/rpiv-ask-user-question/events";
|
|
114
|
+
|
|
115
|
+
pi.events.on(ASK_USER_PROMPT_EVENT, (payload: AskUserPromptEventPayload) => {
|
|
116
|
+
// payload.questions[].{ question, header, multiSelect, options[] }
|
|
117
|
+
// payload.questions[].options[].{ label, description, hasPreview }
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The channel name is `rpiv:ask-user:prompt`. Preview *content* is deliberately not shipped
|
|
122
|
+
in the payload — only `hasPreview: boolean` — so listeners forwarding the event across a
|
|
123
|
+
process or network boundary stay cheap.
|
|
124
|
+
|
|
125
|
+
Stability policy for the `rpiv:*` namespace: channel names are immutable, payload changes
|
|
126
|
+
are append-only and always optional, payloads stay JSON-safe, and any breaking change ships
|
|
127
|
+
as a new channel (e.g. `rpiv:ask-user:prompt.v2`) rather than a version field.
|
package/package.json
CHANGED
|
@@ -1,6 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fadhelhaidar/pi-ask-user",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Standalone Pi questionnaire with combined multi-select and custom answers, Enter-only toggling, and preserved drafts.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "FadhelHaidar",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"pi-package",
|
|
10
|
+
"pi-extension"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/FadhelHaidar/pi-ask-user.git"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./events": {
|
|
22
|
+
"types": "./dist/events.d.ts",
|
|
23
|
+
"default": "./dist/events.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "bun scripts/build.ts",
|
|
28
|
+
"build:check": "bun scripts/build.ts --check",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"typecheck": "tsc -p tests/types/tsconfig.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist/",
|
|
34
|
+
"LICENSE",
|
|
35
|
+
"docs/",
|
|
36
|
+
"!docs/*.png",
|
|
37
|
+
"!docs/*.jpg",
|
|
38
|
+
"!docs/*.svg",
|
|
39
|
+
"README.md",
|
|
40
|
+
"NOTICE.md",
|
|
41
|
+
"!**/*.test.ts"
|
|
42
|
+
],
|
|
43
|
+
"pi": {
|
|
44
|
+
"extensions": [
|
|
45
|
+
"./dist/index.js"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
50
|
+
"@earendil-works/pi-tui": "*",
|
|
51
|
+
"typebox": "*",
|
|
52
|
+
"@juicesharp/rpiv-i18n": "*"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"@juicesharp/rpiv-i18n": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@earendil-works/pi-coding-agent": "1.0.2",
|
|
61
|
+
"@earendil-works/pi-tui": "1.0.2",
|
|
62
|
+
"@juicesharp/rpiv-i18n": "2.12.0",
|
|
63
|
+
"typescript": "5.9.3",
|
|
64
|
+
"vitest": "4.1.10"
|
|
65
|
+
}
|
|
66
|
+
}
|