lite-questionnaire 1.0.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 +358 -0
- package/core.ts +477 -0
- package/design/questionnaire-openapi.yaml +522 -0
- package/index.ts +396 -0
- package/input.ts +513 -0
- package/modules/confirm.ts +42 -0
- package/modules/multiSelect.ts +100 -0
- package/modules/rating.ts +105 -0
- package/modules/select.ts +118 -0
- package/modules/shared.ts +10 -0
- package/modules/text.ts +71 -0
- package/package.json +39 -0
- package/render.ts +319 -0
- package/skills/lite-questionnaire/SKILL.md +276 -0
- package/state.ts +39 -0
- package/types.ts +137 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
openapi: "3.0.3"
|
|
2
|
+
info:
|
|
3
|
+
title: Questionnaire 工具 API
|
|
4
|
+
description: >
|
|
5
|
+
统一问答工具 API,支持单选、多选、文本输入、确认、评分五种问题类型。
|
|
6
|
+
提供条件子问题、约束校验、会话持久化等高级特性。
|
|
7
|
+
version: "2.0.0"
|
|
8
|
+
|
|
9
|
+
servers:
|
|
10
|
+
- url: http://localhost
|
|
11
|
+
description: 本地 pi 工具调用
|
|
12
|
+
|
|
13
|
+
paths:
|
|
14
|
+
/questionnaire:
|
|
15
|
+
post:
|
|
16
|
+
summary: 发起交互式问卷
|
|
17
|
+
description: >
|
|
18
|
+
向用户展示一系列问题,收集用户回答。
|
|
19
|
+
支持多种问题类型(单选/多选/文本/确认/评分),
|
|
20
|
+
支持条件子问题(根据父问题答案动态插入),
|
|
21
|
+
支持声明式约束校验。
|
|
22
|
+
operationId: executeQuestionnaire
|
|
23
|
+
requestBody:
|
|
24
|
+
required: true
|
|
25
|
+
content:
|
|
26
|
+
application/json:
|
|
27
|
+
schema:
|
|
28
|
+
$ref: "#/components/schemas/QuestionnaireParams"
|
|
29
|
+
responses:
|
|
30
|
+
"200":
|
|
31
|
+
description: 用户成功完成并提交问卷
|
|
32
|
+
content:
|
|
33
|
+
application/json:
|
|
34
|
+
schema:
|
|
35
|
+
$ref: "#/components/schemas/QuestionnaireResult"
|
|
36
|
+
"499":
|
|
37
|
+
description: 用户取消问卷(按 Esc)
|
|
38
|
+
content:
|
|
39
|
+
application/json:
|
|
40
|
+
schema:
|
|
41
|
+
type: object
|
|
42
|
+
properties:
|
|
43
|
+
cancelled:
|
|
44
|
+
type: boolean
|
|
45
|
+
description: 固定为 true
|
|
46
|
+
message:
|
|
47
|
+
type: string
|
|
48
|
+
description: 取消消息
|
|
49
|
+
example: "User cancelled the questionnaire"
|
|
50
|
+
|
|
51
|
+
components:
|
|
52
|
+
schemas:
|
|
53
|
+
# ============================================================
|
|
54
|
+
# 顶层请求/响应
|
|
55
|
+
# ============================================================
|
|
56
|
+
|
|
57
|
+
QuestionnaireParams:
|
|
58
|
+
type: object
|
|
59
|
+
description: 问卷配置,包含问题列表
|
|
60
|
+
required:
|
|
61
|
+
- questions
|
|
62
|
+
properties:
|
|
63
|
+
questions:
|
|
64
|
+
type: array
|
|
65
|
+
description: 问题列表,按顺序展示
|
|
66
|
+
minItems: 1
|
|
67
|
+
items:
|
|
68
|
+
$ref: "#/components/schemas/Question"
|
|
69
|
+
|
|
70
|
+
QuestionnaireResult:
|
|
71
|
+
type: object
|
|
72
|
+
description: 问卷提交结果,键为问题 id,值为对应答案
|
|
73
|
+
properties:
|
|
74
|
+
answers:
|
|
75
|
+
type: object
|
|
76
|
+
description: 每个问题的答案,键为问题 id
|
|
77
|
+
additionalProperties:
|
|
78
|
+
$ref: "#/components/schemas/Answer"
|
|
79
|
+
submittedAt:
|
|
80
|
+
type: string
|
|
81
|
+
format: date-time
|
|
82
|
+
description: 提交时间戳
|
|
83
|
+
|
|
84
|
+
# ============================================================
|
|
85
|
+
# 问题类型(多态)
|
|
86
|
+
# ============================================================
|
|
87
|
+
|
|
88
|
+
Question:
|
|
89
|
+
oneOf:
|
|
90
|
+
- $ref: "#/components/schemas/SelectQuestion"
|
|
91
|
+
- $ref: "#/components/schemas/MultiSelectQuestion"
|
|
92
|
+
- $ref: "#/components/schemas/TextQuestion"
|
|
93
|
+
- $ref: "#/components/schemas/ConfirmQuestion"
|
|
94
|
+
- $ref: "#/components/schemas/RatingQuestion"
|
|
95
|
+
discriminator:
|
|
96
|
+
propertyName: type
|
|
97
|
+
mapping:
|
|
98
|
+
select: "#/components/schemas/SelectQuestion"
|
|
99
|
+
multiSelect: "#/components/schemas/MultiSelectQuestion"
|
|
100
|
+
text: "#/components/schemas/TextQuestion"
|
|
101
|
+
confirm: "#/components/schemas/ConfirmQuestion"
|
|
102
|
+
rating: "#/components/schemas/RatingQuestion"
|
|
103
|
+
|
|
104
|
+
# ============================================================
|
|
105
|
+
# 共享基础结构
|
|
106
|
+
# ============================================================
|
|
107
|
+
|
|
108
|
+
BaseQuestion:
|
|
109
|
+
type: object
|
|
110
|
+
description: 所有问题类型的公共基础字段
|
|
111
|
+
required:
|
|
112
|
+
- id
|
|
113
|
+
- label
|
|
114
|
+
- prompt
|
|
115
|
+
properties:
|
|
116
|
+
id:
|
|
117
|
+
type: string
|
|
118
|
+
description: 问题唯一标识,用于结果映射的键名
|
|
119
|
+
example: "language"
|
|
120
|
+
label:
|
|
121
|
+
type: string
|
|
122
|
+
description: Tab 标签栏中显示的短标签
|
|
123
|
+
example: "语言"
|
|
124
|
+
prompt:
|
|
125
|
+
type: string
|
|
126
|
+
description: 完整的问题文本,展示在面板主体区域
|
|
127
|
+
example: "请选择你使用的编程语言"
|
|
128
|
+
constraints:
|
|
129
|
+
type: array
|
|
130
|
+
description: 约束条件列表,声明式校验规则
|
|
131
|
+
items:
|
|
132
|
+
$ref: "#/components/schemas/Constraint"
|
|
133
|
+
children:
|
|
134
|
+
type: array
|
|
135
|
+
description: |
|
|
136
|
+
条件子问题列表。当父问题答案为指定值时,子问题会动态插入到
|
|
137
|
+
Tab 序列中(紧跟在父问题之后)。子问题本身也可以嵌套子问题。
|
|
138
|
+
items:
|
|
139
|
+
$ref: "#/components/schemas/Question"
|
|
140
|
+
showIf:
|
|
141
|
+
type: object
|
|
142
|
+
description: |
|
|
143
|
+
条件显示规则,定义当前问题(子问题)在何种父问题答案下才显示。
|
|
144
|
+
仅用于 children 数组中的子问题,父问题无需此字段。
|
|
145
|
+
required:
|
|
146
|
+
- value
|
|
147
|
+
properties:
|
|
148
|
+
value:
|
|
149
|
+
type: string
|
|
150
|
+
description: 父问题答案匹配值。当父问题的答案等于此值时,当前子问题才会插入 Tab 序列
|
|
151
|
+
example: "ts"
|
|
152
|
+
|
|
153
|
+
Constraint:
|
|
154
|
+
type: object
|
|
155
|
+
description: 声明式约束校验规则,在用户提交问题时进行验证
|
|
156
|
+
required:
|
|
157
|
+
- type
|
|
158
|
+
- message
|
|
159
|
+
properties:
|
|
160
|
+
type:
|
|
161
|
+
type: string
|
|
162
|
+
description: 约束类型
|
|
163
|
+
enum:
|
|
164
|
+
- required
|
|
165
|
+
- minSelect
|
|
166
|
+
- maxSelect
|
|
167
|
+
- minLength
|
|
168
|
+
- maxLength
|
|
169
|
+
- pattern
|
|
170
|
+
value:
|
|
171
|
+
description: |
|
|
172
|
+
约束参数值,具体含义取决于 type:
|
|
173
|
+
- minSelect: 最少选择项数(正整数)
|
|
174
|
+
- maxSelect: 最多选择项数(正整数)
|
|
175
|
+
- minLength: 文本最小长度(正整数)
|
|
176
|
+
- maxLength: 文本最大长度(正整数)
|
|
177
|
+
- pattern: 正则表达式字符串
|
|
178
|
+
- required: 不需要 value(忽略此字段)
|
|
179
|
+
oneOf:
|
|
180
|
+
- type: number
|
|
181
|
+
- type: string
|
|
182
|
+
message:
|
|
183
|
+
type: string
|
|
184
|
+
description: 校验失败时显示的错误提示信息
|
|
185
|
+
example: "至少选择一项"
|
|
186
|
+
|
|
187
|
+
Option:
|
|
188
|
+
type: object
|
|
189
|
+
description: 选项定义,用于 select 和 multiSelect 类型问题
|
|
190
|
+
required:
|
|
191
|
+
- value
|
|
192
|
+
- label
|
|
193
|
+
properties:
|
|
194
|
+
value:
|
|
195
|
+
type: string
|
|
196
|
+
description: 选项的唯一值,作为答案返回
|
|
197
|
+
example: "ts"
|
|
198
|
+
label:
|
|
199
|
+
type: string
|
|
200
|
+
description: 选项的显示文本
|
|
201
|
+
example: "TypeScript"
|
|
202
|
+
description:
|
|
203
|
+
type: string
|
|
204
|
+
description: 选项的可选描述,显示在 label 下方
|
|
205
|
+
|
|
206
|
+
# ============================================================
|
|
207
|
+
# 具体问题类型
|
|
208
|
+
# ============================================================
|
|
209
|
+
|
|
210
|
+
SelectQuestion:
|
|
211
|
+
allOf:
|
|
212
|
+
- $ref: "#/components/schemas/BaseQuestion"
|
|
213
|
+
- type: object
|
|
214
|
+
required:
|
|
215
|
+
- type
|
|
216
|
+
- maxSelect
|
|
217
|
+
- options
|
|
218
|
+
properties:
|
|
219
|
+
type:
|
|
220
|
+
type: string
|
|
221
|
+
enum: [select]
|
|
222
|
+
description: 固定为 `select`
|
|
223
|
+
maxSelect:
|
|
224
|
+
type: integer
|
|
225
|
+
enum: [1]
|
|
226
|
+
default: 1
|
|
227
|
+
description: 固定为 1,表示单选模式
|
|
228
|
+
options:
|
|
229
|
+
type: array
|
|
230
|
+
description: 可选项列表。末尾自动追加"自定义"选项,允许用户输入自定义值
|
|
231
|
+
minItems: 1
|
|
232
|
+
items:
|
|
233
|
+
$ref: "#/components/schemas/Option"
|
|
234
|
+
|
|
235
|
+
MultiSelectQuestion:
|
|
236
|
+
allOf:
|
|
237
|
+
- $ref: "#/components/schemas/BaseQuestion"
|
|
238
|
+
- type: object
|
|
239
|
+
required:
|
|
240
|
+
- type
|
|
241
|
+
- maxSelect
|
|
242
|
+
- options
|
|
243
|
+
properties:
|
|
244
|
+
type:
|
|
245
|
+
type: string
|
|
246
|
+
enum: [multiSelect]
|
|
247
|
+
description: 固定为 `multiSelect`
|
|
248
|
+
maxSelect:
|
|
249
|
+
type: integer
|
|
250
|
+
minimum: 2
|
|
251
|
+
description: 最大可选数量,必须 > 1
|
|
252
|
+
example: 3
|
|
253
|
+
options:
|
|
254
|
+
type: array
|
|
255
|
+
description: 可选项列表。末尾自动追加"自定义"选项
|
|
256
|
+
minItems: 1
|
|
257
|
+
items:
|
|
258
|
+
$ref: "#/components/schemas/Option"
|
|
259
|
+
|
|
260
|
+
TextQuestion:
|
|
261
|
+
allOf:
|
|
262
|
+
- $ref: "#/components/schemas/BaseQuestion"
|
|
263
|
+
- type: object
|
|
264
|
+
required:
|
|
265
|
+
- type
|
|
266
|
+
properties:
|
|
267
|
+
type:
|
|
268
|
+
type: string
|
|
269
|
+
enum: [text]
|
|
270
|
+
description: 固定为 `text`
|
|
271
|
+
placeholder:
|
|
272
|
+
type: string
|
|
273
|
+
description: 输入框占位符文本
|
|
274
|
+
example: "请输入项目描述..."
|
|
275
|
+
multiline:
|
|
276
|
+
type: boolean
|
|
277
|
+
default: false
|
|
278
|
+
description: 是否使用多行文本编辑模式
|
|
279
|
+
|
|
280
|
+
ConfirmQuestion:
|
|
281
|
+
allOf:
|
|
282
|
+
- $ref: "#/components/schemas/BaseQuestion"
|
|
283
|
+
- type: object
|
|
284
|
+
required:
|
|
285
|
+
- type
|
|
286
|
+
properties:
|
|
287
|
+
type:
|
|
288
|
+
type: string
|
|
289
|
+
enum: [confirm]
|
|
290
|
+
description: 固定为 `confirm`
|
|
291
|
+
yesLabel:
|
|
292
|
+
type: string
|
|
293
|
+
default: "是"
|
|
294
|
+
description: 「是」按钮的显示文本
|
|
295
|
+
example: "创建"
|
|
296
|
+
noLabel:
|
|
297
|
+
type: string
|
|
298
|
+
default: "否"
|
|
299
|
+
description: 「否」按钮的显示文本
|
|
300
|
+
example: "取消"
|
|
301
|
+
|
|
302
|
+
RatingQuestion:
|
|
303
|
+
allOf:
|
|
304
|
+
- $ref: "#/components/schemas/BaseQuestion"
|
|
305
|
+
- type: object
|
|
306
|
+
required:
|
|
307
|
+
- type
|
|
308
|
+
- range
|
|
309
|
+
properties:
|
|
310
|
+
type:
|
|
311
|
+
type: string
|
|
312
|
+
enum: [rating]
|
|
313
|
+
description: 固定为 `rating`
|
|
314
|
+
range:
|
|
315
|
+
type: object
|
|
316
|
+
description: 评分范围
|
|
317
|
+
required:
|
|
318
|
+
- min
|
|
319
|
+
- max
|
|
320
|
+
properties:
|
|
321
|
+
min:
|
|
322
|
+
type: integer
|
|
323
|
+
enum: [1]
|
|
324
|
+
description: 固定为 1
|
|
325
|
+
max:
|
|
326
|
+
type: integer
|
|
327
|
+
enum: [5]
|
|
328
|
+
description: 固定为 5
|
|
329
|
+
showEmoji:
|
|
330
|
+
type: boolean
|
|
331
|
+
default: false
|
|
332
|
+
description: 是否在滑块上方显示表情量表(😡 😟 😐 😊 😍)
|
|
333
|
+
annotations:
|
|
334
|
+
type: object
|
|
335
|
+
description: 数值到文字注释的映射,显示在滑块下方
|
|
336
|
+
additionalProperties:
|
|
337
|
+
type: string
|
|
338
|
+
example:
|
|
339
|
+
"1": "非常差"
|
|
340
|
+
"3": "一般"
|
|
341
|
+
"5": "非常好"
|
|
342
|
+
|
|
343
|
+
# ============================================================
|
|
344
|
+
# 答案类型
|
|
345
|
+
# ============================================================
|
|
346
|
+
|
|
347
|
+
Answer:
|
|
348
|
+
oneOf:
|
|
349
|
+
- $ref: "#/components/schemas/SelectAnswer"
|
|
350
|
+
- $ref: "#/components/schemas/MultiSelectAnswer"
|
|
351
|
+
- $ref: "#/components/schemas/TextAnswer"
|
|
352
|
+
- $ref: "#/components/schemas/ConfirmAnswer"
|
|
353
|
+
- $ref: "#/components/schemas/RatingAnswer"
|
|
354
|
+
|
|
355
|
+
SelectAnswer:
|
|
356
|
+
type: object
|
|
357
|
+
description: 单选答案
|
|
358
|
+
properties:
|
|
359
|
+
value:
|
|
360
|
+
type: string
|
|
361
|
+
description: 用户选择的选项值
|
|
362
|
+
example: "ts"
|
|
363
|
+
label:
|
|
364
|
+
type: string
|
|
365
|
+
description: 用户选择的选项显示文本
|
|
366
|
+
example: "TypeScript"
|
|
367
|
+
wasCustom:
|
|
368
|
+
type: boolean
|
|
369
|
+
default: false
|
|
370
|
+
description: 是否来自"自定义"选项的输入
|
|
371
|
+
|
|
372
|
+
MultiSelectAnswer:
|
|
373
|
+
type: object
|
|
374
|
+
description: 多选答案
|
|
375
|
+
properties:
|
|
376
|
+
values:
|
|
377
|
+
type: array
|
|
378
|
+
description: 用户选择的所有选项值
|
|
379
|
+
items:
|
|
380
|
+
type: string
|
|
381
|
+
example: ["ts", "rs"]
|
|
382
|
+
labels:
|
|
383
|
+
type: array
|
|
384
|
+
description: 用户选择的所有选项显示文本
|
|
385
|
+
items:
|
|
386
|
+
type: string
|
|
387
|
+
example: ["TypeScript", "Rust"]
|
|
388
|
+
wasCustom:
|
|
389
|
+
type: boolean
|
|
390
|
+
default: false
|
|
391
|
+
description: 是否包含"自定义"选项的输入
|
|
392
|
+
|
|
393
|
+
TextAnswer:
|
|
394
|
+
type: object
|
|
395
|
+
description: 文本答案
|
|
396
|
+
properties:
|
|
397
|
+
text:
|
|
398
|
+
type: string
|
|
399
|
+
description: 用户输入的文本内容
|
|
400
|
+
example: "一个高性能 API 服务"
|
|
401
|
+
|
|
402
|
+
ConfirmAnswer:
|
|
403
|
+
type: object
|
|
404
|
+
description: 确认答案
|
|
405
|
+
properties:
|
|
406
|
+
confirmed:
|
|
407
|
+
type: boolean
|
|
408
|
+
description: 用户的选择,true = 是/确认,false = 否/取消
|
|
409
|
+
example: true
|
|
410
|
+
label:
|
|
411
|
+
type: string
|
|
412
|
+
description: 用户选中的按钮文本
|
|
413
|
+
example: "创建"
|
|
414
|
+
|
|
415
|
+
RatingAnswer:
|
|
416
|
+
type: object
|
|
417
|
+
description: 评分答案
|
|
418
|
+
properties:
|
|
419
|
+
value:
|
|
420
|
+
type: integer
|
|
421
|
+
minimum: 1
|
|
422
|
+
maximum: 5
|
|
423
|
+
description: 用户选择的评分值(1-5)
|
|
424
|
+
example: 5
|
|
425
|
+
annotation:
|
|
426
|
+
type: string
|
|
427
|
+
description: 对应数值的文字注释(如果配置了 annotations 且该值有注释)
|
|
428
|
+
example: "非常好"
|
|
429
|
+
|
|
430
|
+
# ============================================================
|
|
431
|
+
# 完整请求示例
|
|
432
|
+
# ============================================================
|
|
433
|
+
|
|
434
|
+
examples:
|
|
435
|
+
FullQuestionnaireExample:
|
|
436
|
+
summary: 涵盖所有 5 种问题类型的完整示例
|
|
437
|
+
value:
|
|
438
|
+
questions:
|
|
439
|
+
- id: "language"
|
|
440
|
+
type: "select"
|
|
441
|
+
maxSelect: 1
|
|
442
|
+
label: "语言"
|
|
443
|
+
prompt: "请选择你想使用的编程语言"
|
|
444
|
+
options:
|
|
445
|
+
- value: "ts"
|
|
446
|
+
label: "TypeScript"
|
|
447
|
+
description: "类型安全的 JavaScript 超集"
|
|
448
|
+
- value: "py"
|
|
449
|
+
label: "Python"
|
|
450
|
+
- value: "rs"
|
|
451
|
+
label: "Rust"
|
|
452
|
+
description: "高性能系统编程语言"
|
|
453
|
+
children:
|
|
454
|
+
- id: "framework"
|
|
455
|
+
type: "select"
|
|
456
|
+
maxSelect: 1
|
|
457
|
+
label: "框架"
|
|
458
|
+
prompt: "请选择 Web 框架"
|
|
459
|
+
showIf:
|
|
460
|
+
value: "ts"
|
|
461
|
+
options:
|
|
462
|
+
- value: "react"
|
|
463
|
+
label: "React"
|
|
464
|
+
- value: "vue"
|
|
465
|
+
label: "Vue"
|
|
466
|
+
- value: "svelte"
|
|
467
|
+
label: "Svelte"
|
|
468
|
+
|
|
469
|
+
- id: "features"
|
|
470
|
+
type: "multiSelect"
|
|
471
|
+
maxSelect: 3
|
|
472
|
+
label: "功能"
|
|
473
|
+
prompt: "请选择你需要的功能模块"
|
|
474
|
+
options:
|
|
475
|
+
- value: "auth"
|
|
476
|
+
label: "用户认证"
|
|
477
|
+
- value: "api"
|
|
478
|
+
label: "REST API"
|
|
479
|
+
- value: "ws"
|
|
480
|
+
label: "WebSocket"
|
|
481
|
+
- value: "db"
|
|
482
|
+
label: "数据库"
|
|
483
|
+
- value: "cache"
|
|
484
|
+
label: "缓存"
|
|
485
|
+
constraints:
|
|
486
|
+
- type: "minSelect"
|
|
487
|
+
value: 1
|
|
488
|
+
message: "至少选择一项功能"
|
|
489
|
+
- type: "maxSelect"
|
|
490
|
+
value: 3
|
|
491
|
+
message: "最多选择 3 项功能"
|
|
492
|
+
|
|
493
|
+
- id: "description"
|
|
494
|
+
type: "text"
|
|
495
|
+
label: "描述"
|
|
496
|
+
prompt: "请简要描述你的项目"
|
|
497
|
+
placeholder: "输入项目描述..."
|
|
498
|
+
multiline: true
|
|
499
|
+
constraints:
|
|
500
|
+
- type: "minLength"
|
|
501
|
+
value: 10
|
|
502
|
+
message: "描述至少需要 10 个字符"
|
|
503
|
+
|
|
504
|
+
- id: "license"
|
|
505
|
+
type: "confirm"
|
|
506
|
+
label: "许可"
|
|
507
|
+
prompt: "是否使用 MIT 开源许可证?"
|
|
508
|
+
yesLabel: "是,使用 MIT"
|
|
509
|
+
noLabel: "否,选择其他"
|
|
510
|
+
|
|
511
|
+
- id: "satisfaction"
|
|
512
|
+
type: "rating"
|
|
513
|
+
label: "满意度"
|
|
514
|
+
prompt: "你对当前开发体验的满意度如何?"
|
|
515
|
+
range:
|
|
516
|
+
min: 1
|
|
517
|
+
max: 5
|
|
518
|
+
showEmoji: true
|
|
519
|
+
annotations:
|
|
520
|
+
"1": "非常不满意"
|
|
521
|
+
"3": "一般"
|
|
522
|
+
"5": "非常满意"
|