cognitive-modules-cli 2.2.12 → 2.2.14

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,17 @@
2
2
 
3
3
  All notable changes to this package are documented in this file.
4
4
 
5
+ ## 2.2.14 - 2026-03-08
6
+
7
+ - Release: add docs build to `release:check`, so docs regressions fail before npm publish.
8
+ - Hardening: `packages/cogn` now runs the primary runtime release gate before alias-package publish checks.
9
+ - Providers: preserve Anthropic streaming input/output token accounting correctly.
10
+ - Test coverage: add request-shaping tests for the full stable provider set (`openai`, `anthropic`, `gemini`, `minimax`, `deepseek`, `qwen`).
11
+
12
+ ## 2.2.13 - 2026-02-08
13
+
14
+ - Fix: do not repair/convert successful envelopes into error envelopes when output validation is disabled (`--profile core`/`validate=off`).
15
+
5
16
  ## 2.2.12 - 2026-02-08
6
17
 
7
18
  - Conformance: add `runtime` suite (offline, deterministic vectors) to validate publish-grade JSON parsing and profile gates.
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.12 ...`,避免 PATH/命令冲突。
5
+ Node.js/TypeScript 版本的 Cognitive Modules CLI。文档统一使用明确入口 `npx cogn@2.2.14 ...`,避免 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.12 --help
13
+ npx cogn@2.2.14 --help
14
14
 
15
15
  # 全局安装(可选)
16
- npm install -g cogn@2.2.12
17
- # 或:npm install -g cognitive-modules-cli@2.2.12
16
+ npm install -g cogn@2.2.14
17
+ # 或:npm install -g cognitive-modules-cli@2.2.14
18
18
  ```
19
19
 
20
20
  ## 快速开始
@@ -24,43 +24,56 @@ npm install -g cogn@2.2.12
24
24
  export OPENAI_API_KEY=sk-xxx
25
25
 
26
26
  # 查看 providers 能力矩阵(结构化输出/流式)
27
- npx cogn@2.2.12 providers --pretty
27
+ npx cogn@2.2.14 providers --pretty
28
28
 
29
29
  # 运行模块
30
- npx cogn@2.2.12 run code-reviewer --args "def login(u,p): return db.query(f'SELECT * FROM users WHERE name={u}')" --pretty
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
31
31
 
32
32
  # 列出模块
33
- npx cogn@2.2.12 list
33
+ npx cogn@2.2.14 list
34
34
 
35
35
  # 管道模式
36
- echo "review this code" | npx cogn@2.2.12 pipe --module code-reviewer
36
+ echo "review this code" | npx cogn@2.2.14 pipe --module code-reviewer
37
37
  ```
38
38
 
39
39
  ## 支持的 Provider
40
40
 
41
- | Provider | 环境变量 | 说明 |
41
+ 对外“稳定支持面”收敛为 6 provider(减少认知负担与维护成本):
42
+
43
+ | Provider(Stable) | 环境变量 | 说明 |
42
44
  |----------|----------|------|
43
- | OpenAI | `OPENAI_API_KEY` | OpenAI API |
44
- | Anthropic | `ANTHROPIC_API_KEY` | Claude |
45
+ | OpenAI (ChatGPT) | `OPENAI_API_KEY` | OpenAI API |
46
+ | Anthropic (Claude) | `ANTHROPIC_API_KEY` | Claude |
45
47
  | Gemini | `GEMINI_API_KEY` | Google Gemini |
46
- | DeepSeek | `DEEPSEEK_API_KEY` | DeepSeek |
47
48
  | MiniMax | `MINIMAX_API_KEY` | MiniMax |
48
- | Moonshot | `MOONSHOT_API_KEY` | Kimi |
49
- | Qwen | `DASHSCOPE_API_KEY` / `QWEN_API_KEY` | 通义千问 |
50
- | Ollama | `OLLAMA_HOST` | 本地模型 |
49
+ | DeepSeek | `DEEPSEEK_API_KEY` | DeepSeek API |
50
+ | Qwen (DashScope) | `DASHSCOPE_API_KEY` / `QWEN_API_KEY` | 通义千问 |
51
+
52
+ 实验/社区 provider 仍保留实现,但默认不在文档/能力矩阵中承诺稳定性(需要显式指定 `--provider`):
53
+
54
+ | Provider(Experimental/Community) | 环境变量 | 说明 |
55
+ |----------|----------|------|
56
+ | Moonshot (Kimi) | `MOONSHOT_API_KEY` | experimental |
57
+ | Ollama (local) | `OLLAMA_HOST` | community |
58
+
59
+ 查看全部 provider(含实验/社区):
60
+
61
+ ```bash
62
+ npx cogn@2.2.14 providers --pretty --all
63
+ ```
51
64
 
52
65
  ## 命令
53
66
 
54
67
  ```bash
55
68
  # Core(单文件极简路径)
56
- npx cogn@2.2.12 core new # 生成 demo.md
57
- npx cogn@2.2.12 core run demo.md --args "..." # 运行单文件模块
58
- npx cogn@2.2.12 core promote demo.md # 升级为 v2 模块目录
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 模块目录
59
72
 
60
73
  # 渐进复杂度(Profiles)
61
- npx cogn@2.2.12 run code-reviewer --args "..." --profile core # 极简:跳过校验
62
- npx cogn@2.2.12 run code-reviewer --args "..." --profile standard # 推荐:日常默认
63
- npx cogn@2.2.12 run code-reviewer --args "..." --profile certified # 最严格:v2.2 + 审计 + registry provenance/完整性门禁
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/完整性门禁
64
77
  # 兼容别名(不推荐写进新文档):
65
78
  # - default -> standard
66
79
  # - strict -> standard(deprecated preset)
@@ -70,41 +83,41 @@ npx cogn@2.2.12 run code-reviewer --args "..." --profile certified # 最严格
70
83
  # - --audit(写入 ~/.cognitive/audit/)
71
84
 
72
85
  # 模块操作
73
- npx cogn@2.2.12 list # 列出模块
74
- npx cogn@2.2.12 run <module> --args "..." # 运行模块
75
- npx cogn@2.2.12 add <url> -m <module> # 从 GitHub 添加模块
76
- npx cogn@2.2.12 update <module> # 更新模块
77
- npx cogn@2.2.12 remove <module> # 删除模块
78
- npx cogn@2.2.12 versions <url> # 查看可用版本
79
- npx cogn@2.2.12 init <name> # 创建新模块
80
- npx cogn@2.2.12 pipe --module <name> # 管道模式
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> # 管道模式
81
94
 
82
95
  # 组合执行
83
- npx cogn@2.2.12 compose <module> --args "..."
84
- npx cogn@2.2.12 compose-info <module>
96
+ npx cogn@2.2.14 compose <module> --args "..."
97
+ npx cogn@2.2.14 compose-info <module>
85
98
 
86
99
  # 校验与迁移
87
- npx cogn@2.2.12 validate <module> --v22
88
- npx cogn@2.2.12 validate --all
89
- npx cogn@2.2.12 migrate <module> --dry-run
90
- npx cogn@2.2.12 migrate --all --no-backup
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
91
104
 
92
105
  # 服务器
93
- npx cogn@2.2.12 serve --port 8000 # 启动 HTTP API 服务
94
- npx cogn@2.2.12 mcp # 启动 MCP 服务(Claude Code / Cursor)
106
+ npx cogn@2.2.14 serve --port 8000 # 启动 HTTP API 服务
107
+ npx cogn@2.2.14 mcp # 启动 MCP 服务(Claude Code / Cursor)
95
108
 
96
109
  # 环境检查
97
- npx cogn@2.2.12 doctor
110
+ npx cogn@2.2.14 doctor
98
111
 
99
112
  # Registry(索引与分发)
100
113
  # 默认 registry index(latest):
101
114
  # https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json
102
115
  # 可通过环境变量或全局参数覆盖:
103
- COGNITIVE_REGISTRY_URL=... npx cogn@2.2.12 search
104
- COGNITIVE_REGISTRY_TIMEOUT_MS=15000 COGNITIVE_REGISTRY_MAX_BYTES=2097152 npx cogn@2.2.12 search
105
- npx cogn@2.2.12 search --registry https://github.com/Cognary/cognitive/releases/download/vX.Y.Z/cognitive-registry.v2.json
106
- npx cogn@2.2.12 registry verify --remote --index https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json
107
- npx cogn@2.2.12 registry verify --remote --concurrency 2
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
108
121
  ```
109
122
 
110
123
  ## 开发
package/dist/cli.js CHANGED
@@ -62,14 +62,39 @@ async function main() {
62
62
  process.exit(1);
63
63
  }
64
64
  }
65
- // Get provider
65
+ const commandRequiresProvider = (() => {
66
+ if (command === 'core')
67
+ return positionals[0] === 'run';
68
+ switch (command) {
69
+ case 'run':
70
+ case 'pipe':
71
+ case 'compose':
72
+ case 'test':
73
+ case 'conformance':
74
+ case 'serve':
75
+ case 'mcp':
76
+ return true;
77
+ default:
78
+ return false;
79
+ }
80
+ })();
81
+ // Get provider (only required for commands that actually invoke an LLM).
82
+ // For non-provider commands (list/validate/registry/docs helpers), we keep the CLI usable even
83
+ // when no API keys are configured by using a placeholder provider that should not be invoked.
66
84
  let provider;
67
- try {
68
- provider = getProvider(values.provider, values.model);
85
+ if (commandRequiresProvider) {
86
+ try {
87
+ provider = getProvider(values.provider, values.model);
88
+ }
89
+ catch (e) {
90
+ console.error(`Error: ${e instanceof Error ? e.message : e}`);
91
+ process.exit(1);
92
+ }
69
93
  }
70
- catch (e) {
71
- console.error(`Error: ${e instanceof Error ? e.message : e}`);
72
- process.exit(1);
94
+ else {
95
+ // If the user explicitly asked for a provider, respect it even if unconfigured.
96
+ // Otherwise, pick a stable placeholder.
97
+ provider = getProvider(values.provider ?? 'openai', values.model);
73
98
  }
74
99
  let policy;
75
100
  try {
@@ -248,7 +273,7 @@ async function main() {
248
273
  console.log('');
249
274
  // 2. Provider configuration
250
275
  console.log('LLM Providers:');
251
- const providers = listProviders();
276
+ const providers = listProviders({ all: Boolean(values.all) });
252
277
  let hasConfiguredProvider = false;
253
278
  for (const p of providers) {
254
279
  const status = p.configured ? '✓' : '–';
@@ -257,6 +282,7 @@ async function main() {
257
282
  console.log(` Model: ${p.model}`);
258
283
  console.log(` Structured output: ${p.structuredOutput}`);
259
284
  console.log(` Streaming: ${p.streaming ? 'yes' : 'no'}`);
285
+ console.log(` Support: ${p.support}`);
260
286
  console.log(` Status: ${apiKeyStatus}`);
261
287
  if (p.configured)
262
288
  hasConfiguredProvider = true;
@@ -270,7 +296,8 @@ async function main() {
270
296
  }
271
297
  catch {
272
298
  console.log(' ✗ None configured');
273
- console.log(' → Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, etc.');
299
+ console.log(' → Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, MINIMAX_API_KEY, DEEPSEEK_API_KEY, DASHSCOPE_API_KEY/QWEN_API_KEY');
300
+ console.log(' → Experimental providers require explicit --provider (see: cog providers --all)');
274
301
  }
275
302
  console.log('');
276
303
  // 4. Module scan
@@ -355,7 +382,7 @@ async function main() {
355
382
  break;
356
383
  }
357
384
  case 'providers': {
358
- const providers = listProviders();
385
+ const providers = listProviders({ all: Boolean(values.all) });
359
386
  console.log(JSON.stringify({ providers }, null, values.pretty ? 2 : 0));
360
387
  break;
361
388
  }
@@ -1008,7 +1035,7 @@ OPTIONS:
1008
1035
  -t, --tag <version> Git tag/version (for add/update)
1009
1036
  -b, --branch <name> Git branch (for add)
1010
1037
  -M, --model <name> LLM model (e.g., gpt-4o, gemini-2.0-flash)
1011
- -p, --provider <name> LLM provider (gemini, openai, anthropic, deepseek, minimax, moonshot, qwen, ollama)
1038
+ -p, --provider <name> LLM provider (stable: openai, anthropic, gemini, minimax, deepseek, qwen)
1012
1039
  --pretty Pretty-print JSON output
1013
1040
  -V, --verbose Verbose output
1014
1041
  --no-validate Skip schema validation
@@ -1022,7 +1049,7 @@ OPTIONS:
1022
1049
  --v22 Use strict v2.2 validation (for validate)
1023
1050
  --dry-run Show what would be done without changes (for migrate)
1024
1051
  --no-backup Skip backup before migration (for migrate)
1025
- --all Process all modules (for validate/migrate)
1052
+ --all Process all modules (validate/migrate/test) OR include experimental providers (providers/doctor)
1026
1053
  --conformance Run official spec vectors (for test)
1027
1054
  --suite <name> Conformance suite: envelope|runtime|stream|registry|all
1028
1055
  --level <n> Conformance level: 1|2|3
@@ -1100,15 +1127,17 @@ ENVIRONMENT:
1100
1127
  GEMINI_API_KEY Google Gemini
1101
1128
  OPENAI_API_KEY OpenAI
1102
1129
  ANTHROPIC_API_KEY Anthropic Claude
1103
- DEEPSEEK_API_KEY DeepSeek
1104
1130
  MINIMAX_API_KEY MiniMax
1105
- MOONSHOT_API_KEY Moonshot (Kimi)
1131
+ DEEPSEEK_API_KEY DeepSeek
1106
1132
  DASHSCOPE_API_KEY Alibaba Qwen (通义千问)
1107
- OLLAMA_HOST Ollama local (default: localhost:11434)
1108
1133
  COG_MODEL Override default model for any provider
1109
1134
  COGNITIVE_REGISTRY_URL Override registry index URL
1110
1135
  COGNITIVE_REGISTRY_TIMEOUT_MS Registry index fetch timeout (ms)
1111
1136
  COGNITIVE_REGISTRY_MAX_BYTES Registry index max bytes
1137
+
1138
+ EXPERIMENTAL/COMMUNITY (not in the stable support promise; use --provider explicitly):
1139
+ MOONSHOT_API_KEY Moonshot (Kimi)
1140
+ OLLAMA_HOST Ollama local (default: localhost:11434)
1112
1141
  `);
1113
1142
  }
1114
1143
  main().catch(e => {
@@ -1798,8 +1798,9 @@ export async function runModule(module, provider, options = {}) {
1798
1798
  }
1799
1799
  }
1800
1800
  }
1801
- else if (enableRepair) {
1802
- // Repair error envelopes to ensure they have proper meta fields
1801
+ else if (!response.ok && enableRepair) {
1802
+ // Repair error envelopes to ensure they have proper meta fields.
1803
+ // Important: do not run this on successful envelopes when validation is disabled.
1803
1804
  response = repairErrorEnvelope(response);
1804
1805
  response.version = ENVELOPE_VERSION;
1805
1806
  }
@@ -19,6 +19,8 @@ export interface CallDirective {
19
19
  module: string;
20
20
  args: string;
21
21
  match: string;
22
+ start: number;
23
+ end: number;
22
24
  }
23
25
  export interface SubagentRunOptions {
24
26
  input?: ModuleInput;
@@ -47,7 +49,9 @@ export declare function parseCalls(text: string): CallDirective[];
47
49
  * Replace @call directives with their results
48
50
  */
49
51
  export declare function substituteCallResults(text: string, callResults: Array<{
50
- match: string;
52
+ match?: string;
53
+ start?: number;
54
+ end?: number;
51
55
  module: string;
52
56
  result: unknown;
53
57
  }>): string;
@@ -65,7 +65,9 @@ export function parseCalls(text) {
65
65
  calls.push({
66
66
  module: match[1],
67
67
  args: match[2] || '',
68
- match: match[0]
68
+ match: match[0],
69
+ start: match.index,
70
+ end: match.index + match[0].length
69
71
  });
70
72
  }
71
73
  return calls;
@@ -74,14 +76,38 @@ export function parseCalls(text) {
74
76
  * Replace @call directives with their results
75
77
  */
76
78
  export function substituteCallResults(text, callResults) {
77
- let result = text;
78
- for (const entry of callResults) {
79
+ const buildReplacement = (entry) => {
79
80
  const resultStr = typeof entry.result === 'object'
80
81
  ? JSON.stringify(entry.result, null, 2)
81
82
  : String(entry.result);
82
- const replacement = `[Result from ${entry.module}]:\n${resultStr}`;
83
+ return `[Result from ${entry.module}]:\n${resultStr}`;
84
+ };
85
+ const hasRanges = callResults.every((entry) => typeof entry.start === 'number' && typeof entry.end === 'number');
86
+ if (hasRanges) {
87
+ const sorted = [...callResults].sort((a, b) => (a.start ?? 0) - (b.start ?? 0));
88
+ let cursor = 0;
89
+ let result = '';
90
+ for (const entry of sorted) {
91
+ const start = entry.start;
92
+ const end = entry.end;
93
+ if (start < cursor) {
94
+ continue;
95
+ }
96
+ result += text.slice(cursor, start);
97
+ result += buildReplacement(entry);
98
+ cursor = end;
99
+ }
100
+ result += text.slice(cursor);
101
+ return result;
102
+ }
103
+ let result = text;
104
+ for (const entry of callResults) {
105
+ if (!entry.match) {
106
+ continue;
107
+ }
83
108
  const idx = result.indexOf(entry.match);
84
109
  if (idx !== -1) {
110
+ const replacement = buildReplacement(entry);
85
111
  result = result.slice(0, idx) + replacement + result.slice(idx + entry.match.length);
86
112
  }
87
113
  }
@@ -147,10 +173,22 @@ export class SubagentOrchestrator {
147
173
  }, childContext);
148
174
  // Store result
149
175
  if (childResult.ok && 'data' in childResult) {
150
- callResults.push({ match: call.match, module: call.module, result: childResult.data });
176
+ callResults.push({
177
+ match: call.match,
178
+ start: call.start,
179
+ end: call.end,
180
+ module: call.module,
181
+ result: childResult.data
182
+ });
151
183
  }
152
184
  else if ('error' in childResult) {
153
- callResults.push({ match: call.match, module: call.module, result: { error: childResult.error } });
185
+ callResults.push({
186
+ match: call.match,
187
+ start: call.start,
188
+ end: call.end,
189
+ module: call.module,
190
+ result: { error: childResult.error }
191
+ });
154
192
  }
155
193
  }
156
194
  // Substitute call results into prompt
@@ -20,6 +20,7 @@ export declare class AnthropicProvider extends BaseProvider {
20
20
  * Build request body for Anthropic API
21
21
  */
22
22
  private buildRequestBody;
23
+ private mergeUsage;
23
24
  invoke(params: InvokeParams): Promise<InvokeResult>;
24
25
  /**
25
26
  * Stream-based invoke using Anthropic's streaming API.
@@ -53,6 +53,17 @@ export class AnthropicProvider extends BaseProvider {
53
53
  }
54
54
  return { body, systemContent: systemMessage?.content };
55
55
  }
56
+ mergeUsage(current, next) {
57
+ if (!next)
58
+ return current;
59
+ const promptTokens = next.input_tokens ?? current?.promptTokens ?? 0;
60
+ const completionTokens = next.output_tokens ?? current?.completionTokens ?? 0;
61
+ return {
62
+ promptTokens,
63
+ completionTokens,
64
+ totalTokens: promptTokens + completionTokens,
65
+ };
66
+ }
56
67
  async invoke(params) {
57
68
  if (!this.isConfigured()) {
58
69
  throw new Error('Anthropic API key not configured. Set ANTHROPIC_API_KEY environment variable.');
@@ -140,20 +151,11 @@ export class AnthropicProvider extends BaseProvider {
140
151
  }
141
152
  // Extract usage info (message_delta or message_stop event)
142
153
  if (data.type === 'message_delta' && data.usage) {
143
- usage = {
144
- promptTokens: data.usage.input_tokens || 0,
145
- completionTokens: data.usage.output_tokens || 0,
146
- totalTokens: (data.usage.input_tokens || 0) + (data.usage.output_tokens || 0),
147
- };
154
+ usage = this.mergeUsage(usage, data.usage);
148
155
  }
149
156
  // Also check message_start for input tokens
150
157
  if (data.type === 'message_start' && data.message?.usage) {
151
- const inputTokens = data.message.usage.input_tokens || 0;
152
- usage = {
153
- promptTokens: inputTokens,
154
- completionTokens: usage?.completionTokens || 0,
155
- totalTokens: inputTokens + (usage?.completionTokens || 0),
156
- };
158
+ usage = this.mergeUsage(usage, data.message.usage);
157
159
  }
158
160
  }
159
161
  catch {
@@ -179,19 +181,10 @@ export class AnthropicProvider extends BaseProvider {
179
181
  }
180
182
  }
181
183
  if (data.type === 'message_delta' && data.usage) {
182
- usage = {
183
- promptTokens: data.usage.input_tokens || 0,
184
- completionTokens: data.usage.output_tokens || 0,
185
- totalTokens: (data.usage.input_tokens || 0) + (data.usage.output_tokens || 0),
186
- };
184
+ usage = this.mergeUsage(usage, data.usage);
187
185
  }
188
186
  if (data.type === 'message_start' && data.message?.usage) {
189
- const inputTokens = data.message.usage.input_tokens || 0;
190
- usage = {
191
- promptTokens: inputTokens,
192
- completionTokens: usage?.completionTokens || 0,
193
- totalTokens: inputTokens + (usage?.completionTokens || 0),
194
- };
187
+ usage = this.mergeUsage(usage, data.message.usage);
195
188
  }
196
189
  }
197
190
  catch {
@@ -9,7 +9,10 @@ export class GeminiProvider extends BaseProvider {
9
9
  apiKey;
10
10
  model;
11
11
  baseUrl = 'https://generativelanguage.googleapis.com/v1beta';
12
- constructor(apiKey, model = 'gemini-3-flash') {
12
+ // Default model is intentionally overrideable via `--model`.
13
+ // If this model is not available for a user's account/API version, they should pass an
14
+ // explicit model id (or switch providers) rather than relying on implicit defaults.
15
+ constructor(apiKey, model = 'gemini-3-pro-preview') {
13
16
  super();
14
17
  this.apiKey = apiKey || process.env.GEMINI_API_KEY || '';
15
18
  this.model = model;
@@ -15,9 +15,13 @@ export { DeepSeekProvider } from './deepseek.js';
15
15
  export { MoonshotProvider } from './moonshot.js';
16
16
  export { QwenProvider } from './qwen.js';
17
17
  export { OllamaProvider } from './ollama.js';
18
+ export type ProviderSupportTier = 'stable' | 'experimental' | 'community';
18
19
  export declare function getProvider(name?: string, model?: string): Provider;
19
- export declare function listProviders(): Array<{
20
+ export declare function listProviders(opts?: {
21
+ all?: boolean;
22
+ }): Array<{
20
23
  name: string;
24
+ support: ProviderSupportTier;
21
25
  configured: boolean;
22
26
  model: string;
23
27
  structuredOutput: StructuredOutputMode;
@@ -22,6 +22,14 @@ export { DeepSeekProvider } from './deepseek.js';
22
22
  export { MoonshotProvider } from './moonshot.js';
23
23
  export { QwenProvider } from './qwen.js';
24
24
  export { OllamaProvider } from './ollama.js';
25
+ // Public promise: these providers are the "supported surface" for the open-source npm runtime.
26
+ // Everything else is intentionally treated as experimental/community to reduce user confusion and
27
+ // avoid over-promising stability across a long tail of provider quirks.
28
+ const STABLE_PROVIDERS = ['openai', 'anthropic', 'gemini', 'minimax', 'deepseek', 'qwen'];
29
+ const EXPERIMENTAL_PROVIDERS = ['moonshot', 'ollama'];
30
+ function isStableProvider(name) {
31
+ return STABLE_PROVIDERS.includes(name);
32
+ }
25
33
  const providers = {
26
34
  gemini: (model) => new GeminiProvider(undefined, model),
27
35
  openai: (model) => new OpenAIProvider(undefined, model),
@@ -39,24 +47,27 @@ const providers = {
39
47
  export function getProvider(name, model) {
40
48
  // Check for model override from environment
41
49
  const modelOverride = model || process.env.COG_MODEL;
42
- // Auto-detect if not specified
50
+ // Auto-detect if not specified.
51
+ //
52
+ // Important: we only auto-select within the "stable" provider set.
53
+ // Experimental/community providers can still be used, but require explicit `--provider`,
54
+ // so users don't accidentally end up on a provider they didn't intend (or that isn't
55
+ // part of the stable support promise).
43
56
  if (!name) {
44
- if (process.env.GEMINI_API_KEY)
45
- return new GeminiProvider(undefined, modelOverride);
46
57
  if (process.env.OPENAI_API_KEY)
47
58
  return new OpenAIProvider(undefined, modelOverride);
48
59
  if (process.env.ANTHROPIC_API_KEY)
49
60
  return new AnthropicProvider(undefined, modelOverride);
50
- if (process.env.DEEPSEEK_API_KEY)
51
- return new DeepSeekProvider(undefined, modelOverride);
61
+ if (process.env.GEMINI_API_KEY)
62
+ return new GeminiProvider(undefined, modelOverride);
52
63
  if (process.env.MINIMAX_API_KEY)
53
64
  return new MiniMaxProvider(undefined, modelOverride);
54
- if (process.env.MOONSHOT_API_KEY)
55
- return new MoonshotProvider(undefined, modelOverride);
65
+ if (process.env.DEEPSEEK_API_KEY)
66
+ return new DeepSeekProvider(undefined, modelOverride);
56
67
  if (process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY)
57
68
  return new QwenProvider(undefined, modelOverride);
58
- // Ollama is always available as fallback if nothing else is configured
59
- return new OllamaProvider(modelOverride);
69
+ throw new Error(`No stable provider configured. Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, MINIMAX_API_KEY, DEEPSEEK_API_KEY, DASHSCOPE_API_KEY/QWEN_API_KEY. ` +
70
+ `To use experimental providers, pass --provider (e.g. --provider moonshot).`);
60
71
  }
61
72
  const factory = providers[name.toLowerCase()];
62
73
  if (!factory) {
@@ -70,13 +81,14 @@ function safeCapabilities(p) {
70
81
  return caps;
71
82
  return { structuredOutput: 'prompt', streaming: p.supportsStreaming?.() ?? false };
72
83
  }
73
- function providerRow(name, configured, model, make) {
84
+ function providerRow(name, configured, model, make, support) {
74
85
  const caps = safeCapabilities(make());
75
86
  const structuredJsonSchema = caps.structuredOutput === 'native' && (caps.nativeSchemaDialect ?? 'json-schema') !== 'json-schema'
76
87
  ? 'prompt'
77
88
  : caps.structuredOutput;
78
89
  return {
79
90
  name,
91
+ support,
80
92
  configured,
81
93
  model,
82
94
  structuredOutput: caps.structuredOutput,
@@ -86,15 +98,20 @@ function providerRow(name, configured, model, make) {
86
98
  streaming: caps.streaming,
87
99
  };
88
100
  }
89
- export function listProviders() {
90
- return [
91
- providerRow('gemini', !!process.env.GEMINI_API_KEY, 'gemini-3-flash', () => new GeminiProvider('')),
92
- providerRow('openai', !!process.env.OPENAI_API_KEY, 'gpt-5.2', () => new OpenAIProvider('')),
93
- providerRow('anthropic', !!process.env.ANTHROPIC_API_KEY, 'claude-sonnet-4.5', () => new AnthropicProvider('')),
94
- providerRow('deepseek', !!process.env.DEEPSEEK_API_KEY, 'deepseek-v3.2', () => new DeepSeekProvider('')),
95
- providerRow('minimax', !!process.env.MINIMAX_API_KEY, 'MiniMax-M2.1', () => new MiniMaxProvider('')),
96
- providerRow('moonshot', !!process.env.MOONSHOT_API_KEY, 'kimi-k2.5', () => new MoonshotProvider('')),
97
- providerRow('qwen', !!(process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY), 'qwen3-max', () => new QwenProvider('')),
98
- providerRow('ollama', true, 'llama4 (local)', () => new OllamaProvider()),
101
+ export function listProviders(opts) {
102
+ const stable = [
103
+ providerRow('openai', !!process.env.OPENAI_API_KEY, 'gpt-5.2', () => new OpenAIProvider(''), 'stable'),
104
+ providerRow('anthropic', !!process.env.ANTHROPIC_API_KEY, 'claude-sonnet-4.5', () => new AnthropicProvider(''), 'stable'),
105
+ providerRow('gemini', !!process.env.GEMINI_API_KEY, 'gemini-3-pro-preview', () => new GeminiProvider(''), 'stable'),
106
+ providerRow('minimax', !!process.env.MINIMAX_API_KEY, 'MiniMax-M2.1', () => new MiniMaxProvider(''), 'stable'),
107
+ providerRow('deepseek', !!process.env.DEEPSEEK_API_KEY, 'deepseek-chat', () => new DeepSeekProvider(''), 'stable'),
108
+ providerRow('qwen', !!(process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY), 'qwen3-max', () => new QwenProvider(''), 'stable'),
109
+ ];
110
+ if (!opts?.all)
111
+ return stable;
112
+ const experimental = [
113
+ providerRow('moonshot', !!process.env.MOONSHOT_API_KEY, 'kimi-k2.5', () => new MoonshotProvider(''), 'experimental'),
114
+ providerRow('ollama', !!process.env.OLLAMA_HOST, 'llama4 (local)', () => new OllamaProvider(), 'community'),
99
115
  ];
116
+ return [...stable, ...experimental];
100
117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognitive-modules-cli",
3
- "version": "2.2.12",
3
+ "version": "2.2.14",
4
4
  "description": "Cognitive Modules - Structured AI Task Execution with version management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,7 +25,7 @@
25
25
  "test:watch": "vitest",
26
26
  "test:coverage": "vitest run --coverage",
27
27
  "pack:check": "npm pack --dry-run --json --cache ../../.npm-cache",
28
- "release:check": "npm run build && npm test && npm run pack:check",
28
+ "release:check": "node ../../scripts/release/check-docs-build.js && npm run build && npm test && npm run pack:check",
29
29
  "prepublishOnly": "npm run release:check"
30
30
  },
31
31
  "keywords": [