cognitive-modules-cli 2.2.14 → 2.2.15

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to this package are documented in this file.
4
4
 
5
+ ## 2.2.15 - 2026-03-09
6
+
7
+ - Runtime: add schema-guided output canonicalization so enum labels and unordered arrays can be normalized before validation/repair.
8
+ - Benchmarks: tighten gate/extraction contracts around canonical labels and publish `Benchmark Evidence` showing Gemini + MiniMax contract stability.
9
+ - Use case: add the official `pr-risk-gate` module plus a copy-paste GitHub Actions template for blocking risky PRs in CI.
10
+
5
11
  ## 2.2.14 - 2026-03-08
6
12
 
7
13
  - Release: add docs build to `release:check`, so docs regressions fail before npm publish.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/cognitive-modules-cli.svg)](https://www.npmjs.com/package/cognitive-modules-cli)
4
4
 
5
- Node.js/TypeScript 版本的 Cognitive Modules CLI。文档统一使用明确入口 `npx cogn@2.2.14 ...`,避免 PATH/命令冲突。
5
+ Node.js/TypeScript 版本的 Cognitive Modules CLI。文档统一使用明确入口 `npx cogn@2.2.15 ...`,避免 PATH/命令冲突。
6
6
 
7
7
  > 这是 [cognitive-modules](../../README.md) monorepo 的一部分。
8
8
 
@@ -10,11 +10,11 @@ Node.js/TypeScript 版本的 Cognitive Modules CLI。文档统一使用明确入
10
10
 
11
11
  ```bash
12
12
  # 零安装(推荐)
13
- npx cogn@2.2.14 --help
13
+ npx cogn@2.2.15 --help
14
14
 
15
15
  # 全局安装(可选)
16
- npm install -g cogn@2.2.14
17
- # 或:npm install -g cognitive-modules-cli@2.2.14
16
+ npm install -g cogn@2.2.15
17
+ # 或:npm install -g cognitive-modules-cli@2.2.15
18
18
  ```
19
19
 
20
20
  ## 快速开始
@@ -24,16 +24,16 @@ npm install -g cogn@2.2.14
24
24
  export OPENAI_API_KEY=sk-xxx
25
25
 
26
26
  # 查看 providers 能力矩阵(结构化输出/流式)
27
- npx cogn@2.2.14 providers --pretty
27
+ npx cogn@2.2.15 providers --pretty
28
28
 
29
29
  # 运行模块
30
- npx cogn@2.2.14 run code-reviewer --args "def login(u,p): return db.query(f'SELECT * FROM users WHERE name={u}')" --pretty
30
+ npx cogn@2.2.15 run code-reviewer --args "def login(u,p): return db.query(f'SELECT * FROM users WHERE name={u}')" --pretty
31
31
 
32
32
  # 列出模块
33
- npx cogn@2.2.14 list
33
+ npx cogn@2.2.15 list
34
34
 
35
35
  # 管道模式
36
- echo "review this code" | npx cogn@2.2.14 pipe --module code-reviewer
36
+ echo "review this code" | npx cogn@2.2.15 pipe --module code-reviewer
37
37
  ```
38
38
 
39
39
  ## 支持的 Provider
@@ -59,21 +59,21 @@ echo "review this code" | npx cogn@2.2.14 pipe --module code-reviewer
59
59
  查看全部 provider(含实验/社区):
60
60
 
61
61
  ```bash
62
- npx cogn@2.2.14 providers --pretty --all
62
+ npx cogn@2.2.15 providers --pretty --all
63
63
  ```
64
64
 
65
65
  ## 命令
66
66
 
67
67
  ```bash
68
68
  # Core(单文件极简路径)
69
- npx cogn@2.2.14 core new # 生成 demo.md
70
- npx cogn@2.2.14 core run demo.md --args "..." # 运行单文件模块
71
- npx cogn@2.2.14 core promote demo.md # 升级为 v2 模块目录
69
+ npx cogn@2.2.15 core new # 生成 demo.md
70
+ npx cogn@2.2.15 core run demo.md --args "..." # 运行单文件模块
71
+ npx cogn@2.2.15 core promote demo.md # 升级为 v2 模块目录
72
72
 
73
73
  # 渐进复杂度(Profiles)
74
- npx cogn@2.2.14 run code-reviewer --args "..." --profile core # 极简:跳过校验
75
- npx cogn@2.2.14 run code-reviewer --args "..." --profile standard # 推荐:日常默认
76
- npx cogn@2.2.14 run code-reviewer --args "..." --profile certified # 最严格:v2.2 + 审计 + registry provenance/完整性门禁
74
+ npx cogn@2.2.15 run code-reviewer --args "..." --profile core # 极简:跳过校验
75
+ npx cogn@2.2.15 run code-reviewer --args "..." --profile standard # 推荐:日常默认
76
+ npx cogn@2.2.15 run code-reviewer --args "..." --profile certified # 最严格:v2.2 + 审计 + registry provenance/完整性门禁
77
77
  # 兼容别名(不推荐写进新文档):
78
78
  # - default -> standard
79
79
  # - strict -> standard(deprecated preset)
@@ -83,41 +83,41 @@ npx cogn@2.2.14 run code-reviewer --args "..." --profile certified # 最严格
83
83
  # - --audit(写入 ~/.cognitive/audit/)
84
84
 
85
85
  # 模块操作
86
- npx cogn@2.2.14 list # 列出模块
87
- npx cogn@2.2.14 run <module> --args "..." # 运行模块
88
- npx cogn@2.2.14 add <url> -m <module> # 从 GitHub 添加模块
89
- npx cogn@2.2.14 update <module> # 更新模块
90
- npx cogn@2.2.14 remove <module> # 删除模块
91
- npx cogn@2.2.14 versions <url> # 查看可用版本
92
- npx cogn@2.2.14 init <name> # 创建新模块
93
- npx cogn@2.2.14 pipe --module <name> # 管道模式
86
+ npx cogn@2.2.15 list # 列出模块
87
+ npx cogn@2.2.15 run <module> --args "..." # 运行模块
88
+ npx cogn@2.2.15 add <url> -m <module> # 从 GitHub 添加模块
89
+ npx cogn@2.2.15 update <module> # 更新模块
90
+ npx cogn@2.2.15 remove <module> # 删除模块
91
+ npx cogn@2.2.15 versions <url> # 查看可用版本
92
+ npx cogn@2.2.15 init <name> # 创建新模块
93
+ npx cogn@2.2.15 pipe --module <name> # 管道模式
94
94
 
95
95
  # 组合执行
96
- npx cogn@2.2.14 compose <module> --args "..."
97
- npx cogn@2.2.14 compose-info <module>
96
+ npx cogn@2.2.15 compose <module> --args "..."
97
+ npx cogn@2.2.15 compose-info <module>
98
98
 
99
99
  # 校验与迁移
100
- npx cogn@2.2.14 validate <module> --v22
101
- npx cogn@2.2.14 validate --all
102
- npx cogn@2.2.14 migrate <module> --dry-run
103
- npx cogn@2.2.14 migrate --all --no-backup
100
+ npx cogn@2.2.15 validate <module> --v22
101
+ npx cogn@2.2.15 validate --all
102
+ npx cogn@2.2.15 migrate <module> --dry-run
103
+ npx cogn@2.2.15 migrate --all --no-backup
104
104
 
105
105
  # 服务器
106
- npx cogn@2.2.14 serve --port 8000 # 启动 HTTP API 服务
107
- npx cogn@2.2.14 mcp # 启动 MCP 服务(Claude Code / Cursor)
106
+ npx cogn@2.2.15 serve --port 8000 # 启动 HTTP API 服务
107
+ npx cogn@2.2.15 mcp # 启动 MCP 服务(Claude Code / Cursor)
108
108
 
109
109
  # 环境检查
110
- npx cogn@2.2.14 doctor
110
+ npx cogn@2.2.15 doctor
111
111
 
112
112
  # Registry(索引与分发)
113
113
  # 默认 registry index(latest):
114
114
  # https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json
115
115
  # 可通过环境变量或全局参数覆盖:
116
- COGNITIVE_REGISTRY_URL=... npx cogn@2.2.14 search
117
- COGNITIVE_REGISTRY_TIMEOUT_MS=15000 COGNITIVE_REGISTRY_MAX_BYTES=2097152 npx cogn@2.2.14 search
118
- npx cogn@2.2.14 search --registry https://github.com/Cognary/cognitive/releases/download/vX.Y.Z/cognitive-registry.v2.json
119
- npx cogn@2.2.14 registry verify --remote --index https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json
120
- npx cogn@2.2.14 registry verify --remote --concurrency 2
116
+ COGNITIVE_REGISTRY_URL=... npx cogn@2.2.15 search
117
+ COGNITIVE_REGISTRY_TIMEOUT_MS=15000 COGNITIVE_REGISTRY_MAX_BYTES=2097152 npx cogn@2.2.15 search
118
+ npx cogn@2.2.15 search --registry https://github.com/Cognary/cognitive/releases/download/vX.Y.Z/cognitive-registry.v2.json
119
+ npx cogn@2.2.15 registry verify --remote --index https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json
120
+ npx cogn@2.2.15 registry verify --remote --concurrency 2
121
121
  ```
122
122
 
123
123
  ## 开发
@@ -140,6 +140,27 @@ npm run dev -- run code-reviewer --args "..."
140
140
  npm run release:check
141
141
  ```
142
142
 
143
+ ## Cognitive 对照基准
144
+
145
+ 如果你要验证“使用 Cognitive”和“直接 prompt/skill 风格 JSON 提示”之间的差异,可以运行内置对照基准:
146
+
147
+ ```bash
148
+ # 先确保 dist 已构建
149
+ npm run build
150
+
151
+ # 只看计划,不实际调用模型
152
+ npm run bench:cognitive-vs-raw -- --provider gemini --plan
153
+
154
+ # 实际运行(示例)
155
+ npm run bench:cognitive-vs-raw -- --provider gemini --model gemini-3-pro-preview --runs 3
156
+ ```
157
+
158
+ 相关文件:
159
+
160
+ - `benchmarks/cognitive-vs-raw/README.md`
161
+ - `benchmarks/cognitive-vs-raw/suite.example.json`
162
+ - `benchmarks/cognitive-vs-raw/report-template.md`
163
+
143
164
  ## License
144
165
 
145
166
  MIT
@@ -21,6 +21,74 @@ function safeSnippet(s, max = 500) {
21
21
  return raw;
22
22
  return raw.slice(0, max) + `…(+${raw.length - max} chars)`;
23
23
  }
24
+ function stableCanonicalKey(value) {
25
+ if (Array.isArray(value)) {
26
+ return `[${value.map((item) => stableCanonicalKey(item)).join(',')}]`;
27
+ }
28
+ if (value && typeof value === 'object') {
29
+ const obj = value;
30
+ return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${stableCanonicalKey(obj[key])}`).join(',')}}`;
31
+ }
32
+ return JSON.stringify(value);
33
+ }
34
+ function normalizeStringValue(value, hint) {
35
+ let normalized = value.trim().replace(/\s+/g, ' ');
36
+ switch (hint) {
37
+ case 'lowercase':
38
+ normalized = normalized.toLowerCase();
39
+ break;
40
+ case 'lower_snake_case':
41
+ normalized = normalized
42
+ .toLowerCase()
43
+ .replace(/[^a-z0-9]+/g, '_')
44
+ .replace(/^_+|_+$/g, '');
45
+ break;
46
+ default:
47
+ break;
48
+ }
49
+ return normalized;
50
+ }
51
+ function canonicalizeDataBySchema(value, schema) {
52
+ if (!schema || typeof schema !== 'object' || schema === null) {
53
+ return typeof value === 'string' ? normalizeStringValue(value) : value;
54
+ }
55
+ const schemaObj = schema;
56
+ const type = schemaObj.type;
57
+ if (typeof value === 'string') {
58
+ const hint = typeof schemaObj['x-cognitive-string-normalize'] === 'string'
59
+ ? String(schemaObj['x-cognitive-string-normalize'])
60
+ : undefined;
61
+ const normalized = normalizeStringValue(value, hint);
62
+ if (Array.isArray(schemaObj.enum)) {
63
+ const enumMatch = schemaObj.enum.find((candidate) => {
64
+ if (typeof candidate !== 'string')
65
+ return false;
66
+ return normalizeStringValue(candidate, hint).toLowerCase() === normalized.toLowerCase();
67
+ });
68
+ return enumMatch ?? normalized;
69
+ }
70
+ return normalized;
71
+ }
72
+ if (Array.isArray(value)) {
73
+ const itemSchema = schemaObj.items;
74
+ const normalizedItems = value.map((item) => canonicalizeDataBySchema(item, itemSchema));
75
+ if (schemaObj['x-cognitive-unordered'] === true) {
76
+ normalizedItems.sort((a, b) => stableCanonicalKey(a).localeCompare(stableCanonicalKey(b)));
77
+ }
78
+ return normalizedItems;
79
+ }
80
+ if (value && typeof value === 'object' && !Array.isArray(value) && type === 'object') {
81
+ const properties = (schemaObj.properties && typeof schemaObj.properties === 'object')
82
+ ? schemaObj.properties
83
+ : {};
84
+ const normalized = {};
85
+ for (const [key, child] of Object.entries(value)) {
86
+ normalized[key] = canonicalizeDataBySchema(child, properties[key]);
87
+ }
88
+ return normalized;
89
+ }
90
+ return value;
91
+ }
24
92
  function parseJsonWithCandidates(raw) {
25
93
  const candidates = extractJsonCandidates(raw);
26
94
  const attempts = [];
@@ -936,6 +1004,12 @@ function repairEnvelope(response, riskRule = 'max_changes_risk', maxExplainLengt
936
1004
  if (meta.explain.length > maxExplainLength) {
937
1005
  meta.explain = meta.explain.slice(0, maxExplainLength - 3) + '...';
938
1006
  }
1007
+ // data.rationale is commonly required by module contracts and is safely
1008
+ // synthesizable from meta.explain when the model omitted the longer field.
1009
+ if (typeof data.rationale !== 'string' || data.rationale.trim().length === 0) {
1010
+ data.rationale = meta.explain;
1011
+ repaired.data = data;
1012
+ }
939
1013
  // Build proper v2.2 response with version
940
1014
  const builtMeta = {
941
1015
  confidence: meta.confidence,
@@ -947,8 +1021,9 @@ function repairEnvelope(response, riskRule = 'max_changes_risk', maxExplainLengt
947
1021
  version: ENVELOPE_VERSION,
948
1022
  meta: builtMeta,
949
1023
  // E4000 is an internal/runtime error fallback (should rarely happen after repair).
950
- error: repaired.error ?? { code: 'E4000', message: 'Unknown error' },
951
- partial_data: repaired.partial_data
1024
+ error: repaired.error ??
1025
+ { code: 'E4000', message: typeof meta.explain === 'string' && meta.explain.trim() ? meta.explain : 'Unknown error' },
1026
+ partial_data: repaired.partial_data ?? repaired.data
952
1027
  } : {
953
1028
  ok: true,
954
1029
  version: ENVELOPE_VERSION,
@@ -990,10 +1065,38 @@ function repairErrorEnvelope(data, maxExplainLength = 280) {
990
1065
  explain: meta.explain,
991
1066
  },
992
1067
  // E4000 is an internal/runtime error fallback (should rarely happen after repair).
993
- error: repaired.error ?? { code: 'E4000', message: 'Unknown error' },
994
- partial_data: repaired.partial_data,
1068
+ error: repaired.error ??
1069
+ { code: 'E4000', message: typeof meta.explain === 'string' && meta.explain.trim() ? meta.explain : 'Unknown error' },
1070
+ partial_data: repaired.partial_data ?? repaired.data,
995
1071
  };
996
1072
  }
1073
+ function isPlainRecord(value) {
1074
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
1075
+ }
1076
+ function normalizeEnvelopeLikeObject(parsed) {
1077
+ if (!isPlainRecord(parsed) || !isPlainRecord(parsed.meta) || typeof parsed.ok !== 'boolean') {
1078
+ return parsed;
1079
+ }
1080
+ const normalized = deepClone(parsed);
1081
+ const reserved = new Set(['ok', 'version', 'meta', 'data', 'error', 'partial_data']);
1082
+ const payloadEntries = Object.entries(normalized).filter(([key]) => !reserved.has(key));
1083
+ if (normalized.ok === true) {
1084
+ if (!isPlainRecord(normalized.data) && payloadEntries.length > 0) {
1085
+ normalized.data = Object.fromEntries(payloadEntries);
1086
+ for (const [key] of payloadEntries)
1087
+ delete normalized[key];
1088
+ }
1089
+ return normalized;
1090
+ }
1091
+ const hasError = isPlainRecord(normalized.error) && typeof normalized.error.code === 'string';
1092
+ if (!hasError && payloadEntries.length > 0) {
1093
+ normalized.data = Object.fromEntries(payloadEntries);
1094
+ for (const [key] of payloadEntries)
1095
+ delete normalized[key];
1096
+ normalized.ok = true;
1097
+ }
1098
+ return normalized;
1099
+ }
997
1100
  /**
998
1101
  * Wrap v2.1 response to v2.2 format
999
1102
  */
@@ -1671,14 +1774,15 @@ export async function runModule(module, provider, options = {}) {
1671
1774
  }
1672
1775
  // Convert to v2.2 envelope
1673
1776
  let response;
1674
- if (isV22Envelope(parsed)) {
1675
- response = parsed;
1777
+ const normalizedParsed = normalizeEnvelopeLikeObject(parsed);
1778
+ if (isV22Envelope(normalizedParsed)) {
1779
+ response = normalizedParsed;
1676
1780
  }
1677
- else if (isEnvelopeResponse(parsed)) {
1678
- response = wrapV21ToV22(parsed, riskRule);
1781
+ else if (isEnvelopeResponse(normalizedParsed)) {
1782
+ response = wrapV21ToV22(normalizedParsed, riskRule);
1679
1783
  }
1680
1784
  else {
1681
- response = convertLegacyToEnvelope(parsed);
1785
+ response = convertLegacyToEnvelope(normalizedParsed);
1682
1786
  }
1683
1787
  // Add version and meta fields
1684
1788
  response.version = ENVELOPE_VERSION;
@@ -1727,6 +1831,9 @@ export async function runModule(module, provider, options = {}) {
1727
1831
  // Get data schema (support both "data" and "output" aliases)
1728
1832
  const dataSchema = module.dataSchema || module.outputSchema;
1729
1833
  const metaSchema = module.metaSchema;
1834
+ if (dataSchema) {
1835
+ response.data = canonicalizeDataBySchema(response.data ?? {}, dataSchema);
1836
+ }
1730
1837
  const dataToValidate = response.data ?? {};
1731
1838
  if (dataSchema && Object.keys(dataSchema).length > 0) {
1732
1839
  let dataErrors = validateData(dataToValidate, dataSchema, 'Data');
@@ -1735,7 +1842,8 @@ export async function runModule(module, provider, options = {}) {
1735
1842
  response = repairEnvelope(response, riskRule);
1736
1843
  response.version = ENVELOPE_VERSION;
1737
1844
  // Re-validate after repair
1738
- const repairedData = response.data ?? {};
1845
+ const repairedData = canonicalizeDataBySchema(response.data ?? {}, dataSchema);
1846
+ response.data = repairedData;
1739
1847
  dataErrors = validateData(repairedData, dataSchema, 'Data');
1740
1848
  }
1741
1849
  if (dataErrors.length > 0) {
@@ -2115,14 +2223,15 @@ export async function* runModuleStream(module, provider, options = {}) {
2115
2223
  }
2116
2224
  // Convert to v2.2 envelope
2117
2225
  let response;
2118
- if (isV22Envelope(parsed)) {
2119
- response = parsed;
2226
+ const normalizedParsed = normalizeEnvelopeLikeObject(parsed);
2227
+ if (isV22Envelope(normalizedParsed)) {
2228
+ response = normalizedParsed;
2120
2229
  }
2121
- else if (isEnvelopeResponse(parsed)) {
2122
- response = wrapV21ToV22(parsed, riskRule);
2230
+ else if (isEnvelopeResponse(normalizedParsed)) {
2231
+ response = wrapV21ToV22(normalizedParsed, riskRule);
2123
2232
  }
2124
2233
  else {
2125
- response = convertLegacyToEnvelope(parsed);
2234
+ response = convertLegacyToEnvelope(normalizedParsed);
2126
2235
  }
2127
2236
  // Add version and meta
2128
2237
  response.version = ENVELOPE_VERSION;
@@ -2172,12 +2281,15 @@ export async function* runModuleStream(module, provider, options = {}) {
2172
2281
  const metaSchema = module.metaSchema;
2173
2282
  if (dataSchema && Object.keys(dataSchema).length > 0) {
2174
2283
  let dataToValidate = response.data ?? {};
2284
+ dataToValidate = canonicalizeDataBySchema(dataToValidate, dataSchema);
2285
+ response.data = dataToValidate;
2175
2286
  let dataErrors = validateData(dataToValidate, dataSchema, 'Data');
2176
2287
  if (dataErrors.length > 0 && enableRepair) {
2177
2288
  response = repairEnvelope(response, riskRule);
2178
2289
  response.version = ENVELOPE_VERSION;
2179
2290
  // Re-validate after repair
2180
- const repairedData = response.data ?? {};
2291
+ const repairedData = canonicalizeDataBySchema(response.data ?? {}, dataSchema);
2292
+ response.data = repairedData;
2181
2293
  dataToValidate = repairedData;
2182
2294
  dataErrors = validateData(repairedData, dataSchema, 'Data');
2183
2295
  }
@@ -2,15 +2,17 @@
2
2
  * MiniMax Provider - MiniMax API
3
3
  */
4
4
  import { BaseProvider } from './base.js';
5
+ const DEFAULT_MINIMAX_BASE_URL = 'https://api.minimax.io/v1';
5
6
  export class MiniMaxProvider extends BaseProvider {
6
7
  name = 'minimax';
7
8
  apiKey;
8
9
  model;
9
- baseUrl = 'https://api.minimax.chat/v1';
10
+ baseUrl;
10
11
  constructor(apiKey, model = 'MiniMax-M2.1') {
11
12
  super();
12
13
  this.apiKey = apiKey || process.env.MINIMAX_API_KEY || '';
13
14
  this.model = model;
15
+ this.baseUrl = (process.env.MINIMAX_BASE_URL || DEFAULT_MINIMAX_BASE_URL).replace(/\/+$/, '');
14
16
  }
15
17
  isConfigured() {
16
18
  return !!this.apiKey;
package/dist/types.js CHANGED
@@ -7,7 +7,17 @@
7
7
  // =============================================================================
8
8
  /** Check if response is v2.2 format */
9
9
  export function isV22Envelope(response) {
10
- return 'meta' in response;
10
+ if (typeof response !== 'object' || response === null)
11
+ return false;
12
+ const envelope = response;
13
+ if (typeof envelope.ok !== 'boolean')
14
+ return false;
15
+ if (typeof envelope.meta !== 'object' || envelope.meta === null || Array.isArray(envelope.meta))
16
+ return false;
17
+ if (envelope.ok === true) {
18
+ return 'data' in envelope;
19
+ }
20
+ return typeof envelope.error === 'object' && envelope.error !== null && !Array.isArray(envelope.error);
11
21
  }
12
22
  /** Check if response is successful */
13
23
  export function isEnvelopeSuccess(response) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognitive-modules-cli",
3
- "version": "2.2.14",
3
+ "version": "2.2.15",
4
4
  "description": "Cognitive Modules - Structured AI Task Execution with version management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,6 +24,7 @@
24
24
  "test": "vitest run",
25
25
  "test:watch": "vitest",
26
26
  "test:coverage": "vitest run --coverage",
27
+ "bench:cognitive-vs-raw": "node benchmarks/cognitive-vs-raw/run.mjs",
27
28
  "pack:check": "npm pack --dry-run --json --cache ../../.npm-cache",
28
29
  "release:check": "node ../../scripts/release/check-docs-build.js && npm run build && npm test && npm run pack:check",
29
30
  "prepublishOnly": "npm run release:check"