viatris-exam-cli 0.0.0-stage → 1.4.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/README.md CHANGED
@@ -1,3 +1,237 @@
1
- # Temporary Holding Version
1
+ # Viatris Exam CLI
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 面向 Agent 的 Viatris 考试 OpenAPI 命令行客户端。运行时零第三方依赖,要求 Node.js 18 或更高版本。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm link
9
+ export VIATRIS_API_KEY="replace-with-real-key"
10
+ ```
11
+
12
+ 默认连接线上环境:
13
+
14
+ ```text
15
+ https://apaaspsc.doubaoapps.com/app/app_17gbe211qq5/openapi/
16
+ ```
17
+
18
+ 切换到开发环境:
19
+
20
+ ```bash
21
+ export VIATRIS_API_ENV="development"
22
+ ```
23
+
24
+ 开发环境地址:
25
+
26
+ ```text
27
+ https://apaaspsc-app_17gbe211qq5-1879207268486311.aiforce.run/app/app_17gbe211qq5/openapi/
28
+ ```
29
+
30
+ `VIATRIS_API_ENV` 支持 `production`、`development`,以及别名 `prod`、`dev`。如需临时连接其他部署,可设置 `VIATRIS_API_BASE_URL`;该变量的优先级高于环境预设。
31
+
32
+ CLI 不接受 `--api-key` 参数,避免密钥进入 shell history、进程列表或 Agent 日志。默认使用:
33
+
34
+ ```text
35
+ X-API-Key: <VIATRIS_API_KEY>
36
+ ```
37
+
38
+ 若服务端使用 Bearer Token:
39
+
40
+ ```bash
41
+ export VIATRIS_API_KEY_HEADER="Authorization"
42
+ export VIATRIS_API_KEY_PREFIX="Bearer "
43
+ ```
44
+
45
+ ## Agent 接口约定
46
+
47
+ - API 成功响应以 JSON 写入 `stdout`。
48
+ - 错误以单行结构化 JSON 写入 `stderr`。
49
+ - POST/PUT 请求从文件、`stdin` 或 `--json` 读取 JSON 对象。
50
+ - API Key 只从 `VIATRIS_API_KEY` 环境变量读取。
51
+ - 默认使用线上环境,可通过 `VIATRIS_API_ENV=development` 切换到开发环境。
52
+ - `VIATRIS_API_BASE_URL` 可覆盖当前环境地址。
53
+ - GET 请求遇到网络错误、HTTP 429 或 5xx 时自动重试,POST/PUT/PATCH/DELETE 不自动重试。
54
+ - `describe` 返回机器可读的命令、参数、认证和退出码清单。
55
+ - ID 路径参数自动进行 URL 编码。
56
+ - HTTP 401/403 使用专用退出码,方便 Agent 判断需要刷新认证。
57
+
58
+ ```bash
59
+ viatris-exam describe --compact
60
+ ```
61
+
62
+ 退出码:
63
+
64
+ | 退出码 | 含义 |
65
+ | --- | --- |
66
+ | `0` | 成功 |
67
+ | `1` | API 返回错误 |
68
+ | `2` | 命令、参数、校验或输入错误 |
69
+ | `3` | 环境配置错误 |
70
+ | `4` | 网络或超时错误 |
71
+ | `5` | 认证或授权错误 |
72
+
73
+ ## 命令
74
+
75
+ ### 产品知识树
76
+
77
+ ```bash
78
+ viatris-exam products get "$PRODUCT_ID"
79
+ viatris-exam products update-knowledge-tree "$PRODUCT_ID" \
80
+ --input knowledge-tree.json
81
+ ```
82
+
83
+ 产品是隐藏系统容器,不属于业务知识树层级。固定维度为 L1;知识树回写是完整替换操作,必须一次提交全部 15-500 个 L2/L3/L4 动态节点。先查询产品以获取固定 L1 维度的 `clientRef`。
84
+
85
+ ### 知识节点与文档
86
+
87
+ ```bash
88
+ viatris-exam knowledge tree <product-code>
89
+ viatris-exam knowledge nodes by-path --input path.json
90
+ viatris-exam knowledge nodes create --input node.json
91
+ viatris-exam knowledge nodes get <node-id>
92
+ viatris-exam knowledge nodes update <node-id> --input changes.json
93
+ viatris-exam knowledge nodes move <node-id> --input move.json
94
+ viatris-exam knowledge nodes delete <node-id>
95
+ viatris-exam knowledge documents list <node-id>
96
+ viatris-exam knowledge documents create <node-id> --input document.json
97
+ viatris-exam knowledge documents upsert <node-id> --input documents.json
98
+ viatris-exam knowledge documents update <document-id> --input changes.json
99
+ viatris-exam knowledge documents delete <document-id>
100
+ ```
101
+
102
+ 节点删除会级联删除整棵子树及文档。产品根和固定维度受保护,不能改名、移动或删除。
103
+
104
+ `/knowledge` CRUD 响应中的 `level` 是物理存储层级(产品根为 1、固定维度为 2);产品生成接口使用业务层级(产品隐藏、固定维度为 L1、动态节点为 L2-L4)。
105
+
106
+ ### 出题任务
107
+
108
+ ```bash
109
+ viatris-exam gen-tasks list --status generating --limit 20
110
+ viatris-exam gen-tasks get "$TASK_ID"
111
+ viatris-exam gen-tasks submit "$TASK_ID" --input examples/generated-questions.json
112
+ ```
113
+
114
+ `externalRef` 是题目回写的幂等键。Agent 重试 POST 前应复用原值。
115
+
116
+ 产品全节点任务的 `dimension` 为 `all`。应按 `knowledgeContexts` 覆盖每个叶子节点,并在回写题目时携带对应 `knowledgeNodeId`。
117
+
118
+ ### 考试记录
119
+
120
+ ```bash
121
+ viatris-exam exam-records get "$RECORD_ID"
122
+ viatris-exam exam-records scoring-rules "$RECORD_ID"
123
+ ```
124
+
125
+ 第二个命令返回答卷绑定版本的完整评分规则及用于回写的 `taskId`。
126
+
127
+ ### 评分任务
128
+
129
+ ```bash
130
+ viatris-exam scoring get "$TASK_ID"
131
+ viatris-exam scoring submit "$TASK_ID" --input examples/scoring-results.json
132
+ ```
133
+
134
+ 评分规则 V2 使用稳定的评分点 ID、扣分规则 ID 和题目级封顶规则,由服务端计算最终得分。V1 `pointResults` 结构继续兼容。
135
+
136
+ ### 考试与题库
137
+
138
+ ```bash
139
+ viatris-exam exams create --input exam.json
140
+ viatris-exam questions create < questions.json
141
+ viatris-exam questions version "$QUESTION_ID" --input version.json
142
+ ```
143
+
144
+ ### 医疗知识检索
145
+
146
+ 简单查询可直接使用参数:
147
+
148
+ ```bash
149
+ viatris-exam medical-search search "阿托伐他汀 适应症" \
150
+ --file-type drug \
151
+ --limit 4 \
152
+ --max-length 1200
153
+ ```
154
+
155
+ 也可提交完整 JSON:
156
+
157
+ ```bash
158
+ printf '%s' '{"query":"阿托伐他汀 适应症","limit":4}' |
159
+ viatris-exam medical-search search
160
+ ```
161
+
162
+ 响应中的 `rawJson` 已是结构化对象,可直接读取 `rawJson.documents`,无需再次 `JSON.parse`。
163
+
164
+ ## 错误格式
165
+
166
+ ```json
167
+ {
168
+ "error": {
169
+ "type": "http_error",
170
+ "message": "API request failed with HTTP 422.",
171
+ "details": {
172
+ "status": 422,
173
+ "method": "POST",
174
+ "path": "/openapi/questions",
175
+ "requestId": "request-id",
176
+ "response": {
177
+ "message": "validation failed"
178
+ }
179
+ }
180
+ }
181
+ }
182
+ ```
183
+
184
+ ## 全局参数
185
+
186
+ ```text
187
+ --base-url <url> 覆盖当前环境地址
188
+ --key-header <name> 覆盖 VIATRIS_API_KEY_HEADER
189
+ --key-prefix <value> 覆盖 VIATRIS_API_KEY_PREFIX
190
+ --timeout <ms> 超时时间,默认 30000
191
+ --retries <n> GET 重试次数,默认 2
192
+ --compact 输出单行紧凑 JSON
193
+ ```
194
+
195
+ 地址选择优先级为 `--base-url`、`VIATRIS_API_BASE_URL`、`VIATRIS_API_ENV` 对应的预设地址。认证 Key 仍必须通过 `VIATRIS_API_KEY` 提供。
196
+
197
+ ## 项目结构
198
+
199
+ ```text
200
+ src/
201
+ ├── cli.js # 入口与异常收口
202
+ ├── app.js # 应用编排
203
+ ├── commands.js # 领域命令路由
204
+ ├── command-handlers/
205
+ │ └── *.js # 各领域到 OpenAPI 请求的映射
206
+ ├── args.js # 参数解析与校验
207
+ ├── input.js # JSON 文件、stdin 与内联输入
208
+ ├── config.js # 环境、地址与认证配置
209
+ ├── http.js # HTTP、重试与响应处理
210
+ ├── metadata.js # 版本、命令和环境元数据
211
+ ├── help.js # 帮助文本
212
+ ├── output.js # 标准 JSON 输出
213
+ └── errors.js # 错误类型
214
+ skills/
215
+ └── viatris-exam-cli/
216
+ ├── SKILL.md
217
+ └── references/cli-reference.md
218
+ ```
219
+
220
+ 可分发的 Agent Skill 位于 `skills/viatris-exam-cli/`,与 `src/` 同级并包含在 npm 发布包中。`skills/viatris-exam-cli.zip` 是同步生成的独立分发包。`.trae/skills/viatris-exam-cli` 是指向规范目录的相对符号链接,仅用于本地自动发现。
221
+
222
+ ## 变更规则
223
+
224
+ 任何代码、命令、接口结构、环境地址或文档变化都必须:
225
+
226
+ 1. 更新 `skills/viatris-exam-cli/SKILL.md` 及受影响的参考文件。
227
+ 2. 同步递增 `package.json`、`src/metadata.js` 和 Skill frontmatter 中的版本。
228
+ 3. 保持 `src/cli.js` 为薄入口,将能力放在职责单一的模块中。
229
+ 4. 重新生成 Skill ZIP 并执行验证:
230
+
231
+ ```bash
232
+ npm run pack:skill
233
+ npm test
234
+ npm run check
235
+ npm run check:versions
236
+ npm run check:skill
237
+ ```
package/package.json CHANGED
@@ -1,6 +1,24 @@
1
1
  {
2
2
  "name": "viatris-exam-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.4.0",
4
+ "description": "Agent-friendly CLI for the Viatris exam OpenAPI",
5
+ "type": "module",
6
+ "bin": {
7
+ "viatris-exam": "./src/cli.js"
8
+ },
9
+ "files": [
10
+ "src/",
11
+ "skills/"
12
+ ],
13
+ "scripts": {
14
+ "test": "node --test",
15
+ "check": "node --check src/*.js && node --check src/command-handlers/*.js",
16
+ "check:versions": "node scripts/check-versions.js",
17
+ "check:skill": "node scripts/check-skill.js",
18
+ "pack:skill": "cd skills && /bin/rm -f viatris-exam-cli.zip && /usr/bin/zip -qr viatris-exam-cli.zip viatris-exam-cli -x '*.DS_Store'"
19
+ },
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "license": "UNLICENSED"
24
+ }
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: viatris-exam-cli
3
+ description: Operate the Viatris exam OpenAPI through its agent-friendly CLI. Use for products, knowledge nodes or documents, generation tasks, exam records, scoring, exams, questions, or medical search.
4
+ version: 1.4.0
5
+ ---
6
+
7
+ # Viatris Exam CLI
8
+
9
+ Use the repository-local CLI for every supported API operation. Do not call the HTTP endpoints directly when the CLI covers the task.
10
+
11
+ ## Start
12
+
13
+ 1. Run commands from the repository root.
14
+ 2. Require `VIATRIS_API_KEY` in the environment. Never place the key in arguments, files, output, or logs.
15
+ 3. Use production by default. Set `VIATRIS_API_ENV=development` only when the user requests development or testing.
16
+ 4. Run `node src/cli.js describe --compact` when capability discovery or exact machine-readable metadata is needed.
17
+ 5. Read [references/cli-reference.md](references/cli-reference.md) before constructing a write payload or handling a nonzero exit code.
18
+
19
+ ## Execute
20
+
21
+ - Invoke the CLI as `node src/cli.js ...`. Do not require `npm link`.
22
+ - Prefer `--input <file>` for existing payloads and stdin for generated payloads.
23
+ - Parse successful `stdout` as JSON.
24
+ - Parse failed `stderr` as JSON and branch on both exit code and `error.type`.
25
+ - Preserve `externalRef` when retrying question submission. It is the idempotency key.
26
+ - For product-wide generation tasks, create one or more questions for every entry in `knowledgeContexts` and include its `knowledgeNodeId`.
27
+ - Prefer scoring rule V2 for generated questions and scoring results. Use stable point and deduction-rule IDs.
28
+ - Treat product IDs as hidden containers, not knowledge-tree levels. Fixed dimensions are business L1; generated nodes are L2-L4.
29
+ - Do not automatically retry POST, PUT, PATCH, or DELETE commands. Inspect the returned error and determine whether the server accepted the request.
30
+ - Keep temporary payloads outside the repository unless they are requested deliverables.
31
+
32
+ ## Maintain
33
+
34
+ Treat every repository behavior, command, schema, URL, documentation, or implementation change as a versioned Skill change.
35
+
36
+ For every change:
37
+
38
+ 1. Update `skills/viatris-exam-cli/` and its affected references.
39
+ 2. Increment the semantic version in `package.json`, `src/metadata.js`, and this frontmatter `version`.
40
+ 3. Keep all three versions identical.
41
+ 4. Keep `src/cli.js` as a thin entrypoint. Put capabilities in focused modules under `src/`.
42
+ 5. Keep `.trae/skills/viatris-exam-cli` as a relative symlink to the distributable Skill directory.
43
+ 6. Run `npm run pack:skill` so `skills/viatris-exam-cli.zip` matches the canonical directory.
44
+ 7. Run `npm test`, `npm run check`, `npm run check:versions`, and `npm run check:skill`.
45
+
46
+ Do not finish a code change while the Skill or any version is stale.
@@ -0,0 +1,388 @@
1
+ # CLI Reference
2
+
3
+ ## Authentication And Environment
4
+
5
+ Required:
6
+
7
+ ```bash
8
+ export VIATRIS_API_KEY="..."
9
+ ```
10
+
11
+ Environment selection:
12
+
13
+ | Value | Target |
14
+ | --- | --- |
15
+ | unset, `production`, `prod` | Production |
16
+ | `development`, `dev` | Development |
17
+
18
+ `VIATRIS_API_BASE_URL` overrides the selected environment. Use it only for an explicitly supplied custom deployment or local mock.
19
+
20
+ Default authentication header:
21
+
22
+ ```text
23
+ X-API-Key: <VIATRIS_API_KEY>
24
+ ```
25
+
26
+ For Bearer authentication:
27
+
28
+ ```bash
29
+ export VIATRIS_API_KEY_HEADER="Authorization"
30
+ export VIATRIS_API_KEY_PREFIX="Bearer "
31
+ ```
32
+
33
+ ## Capability Discovery
34
+
35
+ ```bash
36
+ node src/cli.js describe --compact
37
+ node src/cli.js --help
38
+ node src/cli.js --version
39
+ ```
40
+
41
+ `describe` does not require credentials. It returns commands, paths, environment metadata, authentication metadata, and exit codes as JSON.
42
+
43
+ ## Commands
44
+
45
+ Get a product and its fixed L1 dimensions:
46
+
47
+ ```bash
48
+ node src/cli.js products get "$PRODUCT_ID" --compact
49
+ ```
50
+
51
+ The product ID represents a hidden system container and is not part of business levels L1-L4. Use each returned `fixedDimensions[].clientRef` as the parent reference for an L2 node. Each fixed dimension also returns `level: 1` and a display `codePrefix`.
52
+
53
+ Replace a product knowledge tree:
54
+
55
+ ```bash
56
+ node src/cli.js products update-knowledge-tree "$PRODUCT_ID" \
57
+ --input knowledge-tree.json \
58
+ --compact
59
+ ```
60
+
61
+ Payload shape:
62
+
63
+ ```json
64
+ {
65
+ "nodes": [
66
+ {
67
+ "clientRef": "disease.hypertension",
68
+ "parentClientRef": "dimension:disease",
69
+ "level": 2,
70
+ "dimensionCode": "disease",
71
+ "name": "高血压",
72
+ "sortOrder": 1
73
+ },
74
+ {
75
+ "clientRef": "disease.hypertension.diagnosis",
76
+ "parentClientRef": "disease.hypertension",
77
+ "level": 3,
78
+ "dimensionCode": "disease",
79
+ "name": "定义与诊断",
80
+ "sortOrder": 1
81
+ },
82
+ {
83
+ "clientRef": "disease.hypertension.diagnosis.threshold",
84
+ "parentClientRef": "disease.hypertension.diagnosis",
85
+ "level": 4,
86
+ "dimensionCode": "disease",
87
+ "name": "诊断阈值",
88
+ "sortOrder": 1,
89
+ "documents": [
90
+ {
91
+ "title": "药品说明书",
92
+ "url": "https://example.com/source",
93
+ "summary": "与该知识点直接对应的证据摘录"
94
+ }
95
+ ]
96
+ }
97
+ ]
98
+ }
99
+ ```
100
+
101
+ Submit the complete L2/L3/L4 node set, between 15 and 500 nodes. Every `clientRef` must be unique within the payload. L2 nodes use `dimension:<code>` as `parentClientRef`; L3/L4 nodes use their direct parent's `clientRef`. Every L4 node needs at least one document. A successful PUT replaces the previous dynamic nodes, so do not send a partial tree.
102
+
103
+ ### Incremental Knowledge Management
104
+
105
+ Get the complete physical tree and all product documents:
106
+
107
+ ```bash
108
+ node src/cli.js knowledge tree lipitor --compact
109
+ ```
110
+
111
+ The incremental `/knowledge` responses expose physical storage levels where the product root is 1 and fixed dimensions are 2. This differs from the product-generation business taxonomy, where the product is hidden, fixed dimensions are L1, and generated content is L2-L4.
112
+
113
+ Create or reuse a visible path:
114
+
115
+ ```bash
116
+ node src/cli.js knowledge nodes by-path --json \
117
+ '{"product":"lipitor","path":["疾病知识","血脂异常基础","ASCVD 风险评估"]}'
118
+ ```
119
+
120
+ The path has one to three entries. The first entry must be a fixed dimension name. The response separates all resolved `nodes` from nodes newly `created`.
121
+
122
+ Create or reuse one node under a known parent:
123
+
124
+ ```bash
125
+ node src/cli.js knowledge nodes create --json \
126
+ '{"parentId":"PARENT_ID","name":"ASCVD 风险评估","sortOrder":20}'
127
+ ```
128
+
129
+ Read, update, move, or delete a node:
130
+
131
+ ```bash
132
+ node src/cli.js knowledge nodes get "$NODE_ID"
133
+ node src/cli.js knowledge nodes update "$NODE_ID" \
134
+ --json '{"name":"血脂异常与他汀治疗","sortOrder":5}'
135
+ node src/cli.js knowledge nodes move "$NODE_ID" \
136
+ --json '{"newParentId":"NEW_PARENT_ID","sortOrder":30}'
137
+ node src/cli.js knowledge nodes delete "$NODE_ID"
138
+ ```
139
+
140
+ Product roots and fixed dimensions are protected from rename, move, and deletion. Moving cannot cross products, target the node itself or its descendants, or produce levels deeper than L4. Deleting a node deletes its entire subtree and attached documents; inspect the node first and only delete when explicitly required.
141
+
142
+ Manage documents:
143
+
144
+ ```bash
145
+ node src/cli.js knowledge documents list "$NODE_ID"
146
+ node src/cli.js knowledge documents create "$NODE_ID" \
147
+ --json '{"title":"说明书","url":"https://example.com/source","summary":"摘录"}'
148
+ node src/cli.js knowledge documents upsert "$NODE_ID" \
149
+ --json '{"items":[{"title":"说明书","summary":"更新后的摘录"}]}'
150
+ node src/cli.js knowledge documents update "$DOCUMENT_ID" \
151
+ --json '{"url":null,"summary":"更新后的摘要"}'
152
+ node src/cli.js knowledge documents delete "$DOCUMENT_ID"
153
+ ```
154
+
155
+ Create reuses a document with the same title under the node. Batch upsert accepts 1-20 items and updates `url` and `summary` for existing titles. A single-document update changes only supplied fields; send `null` to clear `url` or `summary`. Inspect a document before an explicitly requested delete.
156
+
157
+ List generation tasks:
158
+
159
+ ```bash
160
+ node src/cli.js gen-tasks list \
161
+ --status generating \
162
+ --limit 20 \
163
+ --compact
164
+ ```
165
+
166
+ `--status` accepts `generating`, `partial_failed`, or `failed`. `--limit` accepts 1 through 50.
167
+
168
+ Get one generation task:
169
+
170
+ ```bash
171
+ node src/cli.js gen-tasks get "$TASK_ID" --compact
172
+ ```
173
+
174
+ For a leaf-node task, `knowledgeContext` is the mandatory target context. For a product-wide task, `dimension` is `all`, `knowledgeContext` is null, and `knowledgeContexts` contains every leaf context. Generate `questionsPerNode` questions for every listed context and follow all returned `rules`.
175
+
176
+ Submit generated questions:
177
+
178
+ ```bash
179
+ node src/cli.js gen-tasks submit "$TASK_ID" --input payload.json --compact
180
+ ```
181
+
182
+ Payload shape:
183
+
184
+ ```json
185
+ {
186
+ "items": [
187
+ {
188
+ "externalRef": "agent-q-0001",
189
+ "failureId": "optional-failure-id",
190
+ "knowledgeNodeId": "required-for-product-wide-task",
191
+ "content": {
192
+ "stem": "Question stem",
193
+ "referenceAnswer": "Reference answer",
194
+ "scoringPoints": [
195
+ {
196
+ "id": "dose-selection",
197
+ "point": "Scoring point",
198
+ "maxScore": 4,
199
+ "criteria": "Choose one deduction level",
200
+ "deductionRules": [
201
+ {
202
+ "id": "dose-selection-full",
203
+ "level": "full",
204
+ "deductScore": 0,
205
+ "criteria": "Complete answer"
206
+ },
207
+ {
208
+ "id": "dose-selection-zero",
209
+ "level": "zero",
210
+ "deductScore": 4,
211
+ "criteria": "Wrong or missing answer"
212
+ }
213
+ ]
214
+ }
215
+ ],
216
+ "scoringRuleVersion": 2,
217
+ "scoreCapRules": [
218
+ {
219
+ "id": "critical-error-1",
220
+ "description": "Critical contradictory conclusion",
221
+ "maxTotalScore": 2
222
+ }
223
+ ],
224
+ "evidence": [
225
+ {
226
+ "source": "Source",
227
+ "citation": "Citation",
228
+ "relevance": "Relevance"
229
+ }
230
+ ]
231
+ },
232
+ "qcResult": {
233
+ "outOfScope": false,
234
+ "insufficientEvidence": false,
235
+ "answerConflict": false,
236
+ "duplicateRisk": false,
237
+ "summary": "No obvious issue"
238
+ }
239
+ }
240
+ ],
241
+ "finished": true
242
+ }
243
+ ```
244
+
245
+ Omit `knowledgeNodeId` for ordinary tasks. For a product-wide task it is required and must match a node in the task's `knowledgeContexts`.
246
+
247
+ Get a submitted exam record and its frozen answers:
248
+
249
+ ```bash
250
+ node src/cli.js exam-records get "$RECORD_ID" --compact
251
+ ```
252
+
253
+ Get the complete scoring rules bound to the record:
254
+
255
+ ```bash
256
+ node src/cli.js exam-records scoring-rules "$RECORD_ID" --compact
257
+ ```
258
+
259
+ The response includes the scoring `taskId`, `schemaVersion`, frozen question versions, evidence, V2 deduction rules, score caps, and item IDs. Use its `taskId` for scoring submission.
260
+
261
+ Get a scoring task:
262
+
263
+ ```bash
264
+ node src/cli.js scoring get "$TASK_ID" --compact
265
+ ```
266
+
267
+ Submit scoring results:
268
+
269
+ ```bash
270
+ node src/cli.js scoring submit "$TASK_ID" --input results.json --compact
271
+ ```
272
+
273
+ Payload shape:
274
+
275
+ ```json
276
+ {
277
+ "items": [
278
+ {
279
+ "itemId": "item-id",
280
+ "ok": true,
281
+ "pointJudgements": [
282
+ {
283
+ "pointId": "dose-selection",
284
+ "appliedDeductionRuleId": "dose-selection-full",
285
+ "reason": "Reason for selecting this deduction level",
286
+ "quote": "Supporting answer quote"
287
+ }
288
+ ],
289
+ "triggeredScoreCaps": [],
290
+ "flags": [],
291
+ "needReview": false
292
+ }
293
+ ]
294
+ }
295
+ ```
296
+
297
+ For V2, submit exactly one `pointJudgement` per scoring point. IDs must match the fetched rule set. Add triggered cap rule IDs to `triggeredScoreCaps`; the server calculates the score, so `total` is optional and must match if supplied. V1 remains compatible through `pointResults` and required `total`. For a failed item, set `ok` to `false` and provide `failureReason`.
298
+
299
+ Create an exam:
300
+
301
+ ```bash
302
+ node src/cli.js exams create --input exam.json --compact
303
+ ```
304
+
305
+ Required top-level fields are `name`, `product`, `questionCount`, `startAt`, `endAt`, `resultPolicy`, and `assigneeIds`. `questionCount` is 1 through 150. Use either `questions` or legacy `questionIds`, never both. Each question score must be a positive integer and the complete exam must total exactly 100.
306
+
307
+ Create questions directly:
308
+
309
+ ```bash
310
+ node src/cli.js questions create --input questions.json --compact
311
+ ```
312
+
313
+ Payload shape is `{ "items": [...] }`. Each item requires `product`, `dimension`, and `content`; provide `externalRef` for idempotency. Prefer `scoringRuleVersion: 2`, stable IDs on scoring points and deduction rules, and explicit `scoreCapRules`.
314
+
315
+ The API has partial-success semantics: inspect `accepted`, `duplicates`, and every entry in `rejected` even when the HTTP response is successful.
316
+
317
+ Add a question version:
318
+
319
+ ```bash
320
+ node src/cli.js questions version "$QUESTION_ID" --input version.json --compact
321
+ ```
322
+
323
+ Required fields are `stem`, `referenceAnswer`, nonempty `scoringPoints`, `evidence`, and `editReason`. V2 versions may also include `scoringRuleVersion: 2`, deduction rules, and `scoreCapRules`.
324
+
325
+ Search medical evidence:
326
+
327
+ ```bash
328
+ node src/cli.js medical-search search "阿托伐他汀 适应症" \
329
+ --file-type drug \
330
+ --limit 4 \
331
+ --max-length 1200 \
332
+ --compact
333
+ ```
334
+
335
+ Or use JSON:
336
+
337
+ ```bash
338
+ printf '%s' '{"query":"阿托伐他汀 适应症","limit":4}' |
339
+ node src/cli.js medical-search search --compact
340
+ ```
341
+
342
+ Do not combine query flags with JSON input.
343
+
344
+ The response `rawJson` is already a structured object. Read `rawJson.documents` directly; do not call `JSON.parse`. Each result contains `rank`, `doc_info`, optional `medical_info`, and the upstream `logid`.
345
+
346
+ ## Input Rules
347
+
348
+ Choose exactly one input form:
349
+
350
+ ```bash
351
+ node src/cli.js questions create --input payload.json
352
+ node src/cli.js questions create --input - < payload.json
353
+ node src/cli.js questions create --json '{"items":[]}'
354
+ printf '%s' '{"items":[]}' | node src/cli.js questions create
355
+ ```
356
+
357
+ The top-level JSON value must be an object.
358
+
359
+ ## Output And Errors
360
+
361
+ Success writes API JSON to `stdout`. Errors write one JSON object to `stderr`:
362
+
363
+ ```json
364
+ {
365
+ "error": {
366
+ "type": "http_error",
367
+ "message": "API request failed with HTTP 422.",
368
+ "details": {
369
+ "status": 422,
370
+ "method": "POST",
371
+ "path": "/openapi/questions",
372
+ "requestId": "optional-request-id",
373
+ "response": {}
374
+ }
375
+ }
376
+ }
377
+ ```
378
+
379
+ | Exit | Meaning | Agent action |
380
+ | --- | --- | --- |
381
+ | `0` | Success | Parse stdout JSON |
382
+ | `1` | API error | Inspect status and response |
383
+ | `2` | Usage, validation, or input error | Correct command or payload |
384
+ | `3` | Configuration error | Check environment configuration |
385
+ | `4` | Network or timeout error | GET may be retried; inspect write state first |
386
+ | `5` | Authentication or authorization error | Refresh or correct credentials |
387
+
388
+ GET automatically retries network errors, HTTP 429, and HTTP 5xx. POST, PUT, PATCH, and DELETE never retry automatically.