opencode-cmd-provider 1.0.2 → 1.1.1

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
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.1 - 2026-08-20
4
+
5
+ Fix: generate image-input modalities from the Command Code CLI catalog.
6
+
7
+ - `MODEL_INPUT_MODALITIES` now comes from `inputModalities` fields in the
8
+ parsed `command-code` CLI bundle instead of a hand-maintained table.
9
+ - The release refresh validates that every API snapshot model is represented
10
+ in the CLI bundle and fails loudly on unsupported or conflicting modality
11
+ data.
12
+ - Added AST parser and offline coverage for reordered fields, duplicate model
13
+ entries, malformed bundles, and text-only fallback behavior.
14
+ - Docs: the release skill and facts-sync spec now describe the modality
15
+ refresh and the new release-gate failure mode.
16
+ - Thanks to @ericpastorm for #31, which auto-syncs the input-modality table
17
+ from the CLI catalog.
18
+
19
+ ## 1.1.0 - 2026-08-20
20
+
21
+ Feature: complete capability metadata for every catalog model, with automatic sync of reasoning and pricing facts at release time.
22
+
23
+ - Every auto-registered model now advertises tool calls, reasoning (efforts or reasoning-capable classification), image input, and cost — no more blank CAPABILITIES, blank MODALITIES, or $0.00 rows (fixes #22).
24
+ - Reasoning efforts and per-1M-token rates are generated from the command-code package's bundled `models.md` at snapshot-refresh time (`src/catalog/facts.ts`); the release pipeline now fails loudly if the committed facts drifted (ADR 0003).
25
+ - Corrected real pricing drift: `deepseek-v4-pro`/`flash` rates were stale, and the expired `gpt-5.6-terra`/`luna` discounts are gone. Context-tier pricing was removed — Command Code publishes flat rates only.
26
+ - Added vision entries for `Qwen/Qwen3.8-27B` and `google/gemini-3.7-flash`, and reasoning efforts for `zai-org/GLM-5.3`, `Qwen/Qwen3.8-27B`, `google/gemini-3.7-flash`, and `xai/grok-4.6`.
27
+
3
28
  ## 1.0.2 - 2026-08-19
4
29
 
5
30
  Chore: refresh the bundled model catalog snapshot after the live catalog drifted.
package/README.md CHANGED
@@ -113,12 +113,14 @@ bundled, and the plugin never contacts the Command Code API to list models. The
113
113
  plugin works fully offline; model availability never depends on the catalog
114
114
  endpoint being reachable.
115
115
 
116
- The following environment variable is intended for tests, local mocks, and
116
+ The following environment variables are intended for tests, local mocks, and
117
117
  compatible API endpoints:
118
118
 
119
- | Variable | Purpose |
120
- | ---------------------- | -------------------------------------- |
121
- | `COMMANDCODE_API_BASE` | Override the Command Code API base URL |
119
+ | Variable | Purpose |
120
+ | ---------------------------- | -------------------------------------- |
121
+ | `COMMANDCODE_API_BASE` | Override the Command Code API base URL |
122
+ | `COMMANDCODE_FACTS_URL` | Override the bundled `models.md` URL |
123
+ | `COMMANDCODE_MODALITIES_URL` | Override the CLI bundle URL |
122
124
 
123
125
  ### Reasoning support
124
126
 
@@ -128,13 +130,13 @@ Reasoning blocks from completed assistant turns are not replayed to Command Code
128
130
 
129
131
  ## Image input
130
132
 
131
- Image input is advertised only for models marked with the `image` input modality in the Command Code model catalog. The capability snapshot follows the current official CLI catalog; unknown models default to text-only until their upstream metadata is reviewed.
133
+ Image input is advertised only for models marked with the `image` input modality in the Command Code CLI bundle. The release-time refresh generates this map into `src/catalog/facts.ts`; unknown models default to text-only until their upstream metadata is reviewed.
132
134
 
133
135
  For vision-capable models, image blocks from user messages and tool results are forwarded in Command Code's data-URL wire format. Text-only models reject image content before making a network request instead of silently dropping it.
134
136
 
135
137
  ## Pricing display
136
138
 
137
- The Command Code Provider API does not currently include prices in its model catalog. This provider keeps a static table for models with known prices so OpenCode can display estimated request costs.
139
+ The Command Code Provider API does not currently include prices in its model catalog. This provider generates a table from the bundled `models.md` catalog so OpenCode can display estimated request costs.
138
140
 
139
141
  Models missing from that table display zero cost in OpenCode. This does **not** mean Command Code will bill the request at zero. Check the current [Command Code pricing](https://commandcode.ai/docs/resources/pricing-limits) before relying on the displayed value.
140
142
 
@@ -0,0 +1,12 @@
1
+ export declare const FACTS_SOURCE_URL = "https://unpkg.com/command-code@1.28.1/dist/bundled/command-code-knowledge/reference/models.md";
2
+ export declare const MODALITIES_SOURCE_URL = "https://unpkg.com/command-code@1.28.1/dist/cli.mjs";
3
+ export declare const FACTS_PACKAGE_VERSION = "1.28.1";
4
+ export declare const FACTS_LAST_REFRESHED = "2026-08-19";
5
+ export declare const MODEL_EFFORTS: Readonly<Record<string, readonly string[]>>;
6
+ export declare const MODEL_COSTS: Readonly<Record<string, {
7
+ input: number;
8
+ output: number;
9
+ cacheRead: number;
10
+ cacheWrite: number;
11
+ }>>;
12
+ export declare const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly ("text" | "image")[]>>;
@@ -0,0 +1,138 @@
1
+ // src/catalog/facts.ts — GENERATED by scripts/refresh-snapshot.mjs. Do not edit.
2
+ //
3
+ // Capability facts (reasoning efforts + per-1M-token rates) parsed from the
4
+ // command-code npm package's bundled model catalog (models.md), plus input
5
+ // modalities parsed from its CLI bundle (dist/cli.mjs). Regenerate
6
+ // with `npm run refresh:snapshot`.
7
+ export const FACTS_SOURCE_URL = "https://unpkg.com/command-code@1.28.1/dist/bundled/command-code-knowledge/reference/models.md";
8
+ export const MODALITIES_SOURCE_URL = "https://unpkg.com/command-code@1.28.1/dist/cli.mjs";
9
+ export const FACTS_PACKAGE_VERSION = "1.28.1";
10
+ export const FACTS_LAST_REFRESHED = "2026-08-19";
11
+ export const MODEL_EFFORTS = {
12
+ "deepseek/deepseek-v4-pro": ["high", "max"],
13
+ "deepseek/deepseek-v4-flash": ["high", "max"],
14
+ "zai-org/GLM-5.3": ["low", "high", "max"],
15
+ "zai-org/GLM-5.2": ["high", "max"],
16
+ "Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"],
17
+ "Qwen/Qwen3.8-27B": ["low", "medium", "xhigh"],
18
+ "claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"],
19
+ "claude-sonnet-4-6": ["low", "medium", "high", "xhigh", "max"],
20
+ "claude-fable-5": ["low", "medium", "high", "xhigh", "max"],
21
+ "claude-opus-5": ["low", "medium", "high", "xhigh", "max"],
22
+ "claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"],
23
+ "claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"],
24
+ "gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max"],
25
+ "gpt-5.6-terra": ["low", "medium", "high", "xhigh", "max"],
26
+ "gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"],
27
+ "gpt-5.5": ["low", "medium", "high", "xhigh"],
28
+ "gpt-5.4": ["low", "medium", "high", "xhigh"],
29
+ "gpt-5.3-codex": ["low", "medium", "high", "xhigh"],
30
+ "gpt-5.4-mini": ["low", "medium", "high"],
31
+ "google/gemini-3.7-flash": ["low", "medium", "high"],
32
+ "google/gemini-3.6-flash": ["low", "medium", "high"],
33
+ "google/gemini-3.5-flash": ["low", "medium", "high"],
34
+ "google/gemini-3.5-flash-lite": ["low", "medium", "high"],
35
+ "google/gemini-3.1-flash-lite": ["low", "medium", "high"],
36
+ "sakana/fugu-ultra": ["high", "xhigh"],
37
+ "xai/grok-4.5": ["low", "medium", "high"],
38
+ "xai/grok-4.6": ["low", "medium", "high", "xhigh"],
39
+ };
40
+ export const MODEL_COSTS = {
41
+ "deepseek/deepseek-v4-pro": { input: 0.66, output: 1.98, cacheRead: 0.022, cacheWrite: 0 },
42
+ "deepseek/deepseek-v4-flash": { input: 0.22, output: 0.66, cacheRead: 0.007, cacheWrite: 0 },
43
+ "moonshotai/Kimi-K3": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 0 },
44
+ "moonshotai/Kimi-K2.7-Code": { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0 },
45
+ "moonshotai/Kimi-K2.7-Code-Highspeed": { input: 1.9, output: 8, cacheRead: 0.38, cacheWrite: 0 },
46
+ "moonshotai/Kimi-K2.6": { input: 0.95, output: 4, cacheRead: 0.16, cacheWrite: 0 },
47
+ "moonshotai/Kimi-K2.5": { input: 0.6, output: 3, cacheRead: 0.1, cacheWrite: 0 },
48
+ "zai-org/GLM-5.3": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
49
+ "zai-org/GLM-5.2": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
50
+ "zai-org/GLM-5.2-Fast": { input: 3, output: 10.25, cacheRead: 0.5, cacheWrite: 0 },
51
+ "zai-org/GLM-5.1": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
52
+ "zai-org/GLM-5": { input: 1, output: 3.2, cacheRead: 0.2, cacheWrite: 0 },
53
+ "MiniMaxAI/MiniMax-M3": { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0 },
54
+ "MiniMaxAI/MiniMax-M2.7": { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0 },
55
+ "MiniMaxAI/MiniMax-M2.5": { input: 0.3, output: 1.2, cacheRead: 0.03, cacheWrite: 0 },
56
+ "xiaomi/mimo-v2.5-pro": { input: 0.435, output: 0.87, cacheRead: 0.0036, cacheWrite: 0 },
57
+ "xiaomi/mimo-v2.5": { input: 0.14, output: 0.28, cacheRead: 0.0028, cacheWrite: 0 },
58
+ "Qwen/Qwen3.8-Max": { input: 2, output: 6, cacheRead: 0.25, cacheWrite: 2.5 },
59
+ "Qwen/Qwen3.8-27B": { input: 0.4, output: 3, cacheRead: 0.04, cacheWrite: 0 },
60
+ "Qwen/Qwen3.7-Max": { input: 2.5, output: 7.5, cacheRead: 0.5, cacheWrite: 3.13 },
61
+ "Qwen/Qwen3.7-Plus": { input: 0.4, output: 1.6, cacheRead: 0.08, cacheWrite: 0.5 },
62
+ "Qwen/Qwen3.7-Flash": { input: 0.03, output: 0.13, cacheRead: 0.006, cacheWrite: 0.038 },
63
+ "Qwen/Qwen3.6-Max-Preview": { input: 1.3, output: 7.8, cacheRead: 0.26, cacheWrite: 1.63 },
64
+ "Qwen/Qwen3.6-Plus": { input: 0.5, output: 3, cacheRead: 0.1, cacheWrite: 0 },
65
+ "stepfun/Step-3.7-Flash": { input: 0.2, output: 1.15, cacheRead: 0.04, cacheWrite: 0 },
66
+ "stepfun/Step-3.5-Flash": { input: 0.1, output: 0.3, cacheRead: 0.02, cacheWrite: 0 },
67
+ "tencent/hy3-paid": { input: 0.14, output: 0.58, cacheRead: 0.035, cacheWrite: 0 },
68
+ "nvidia/nemotron-3-ultra-550b-a55b": { input: 0.6, output: 2.4, cacheRead: 0.12, cacheWrite: 0 },
69
+ "thinkingmachines/inkling": { input: 1, output: 4.05, cacheRead: 0.17, cacheWrite: 0 },
70
+ "thinkingmachines/inkling-small": { input: 0.5, output: 1.2, cacheRead: 0.1, cacheWrite: 0 },
71
+ "poolside/laguna-s-2.1-free": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
72
+ "claude-sonnet-5": { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 },
73
+ "claude-sonnet-4-6": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
74
+ "claude-fable-5": { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
75
+ "claude-opus-5": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
76
+ "claude-opus-4-8": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
77
+ "claude-opus-4-7": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
78
+ "claude-haiku-4-5-20251001": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
79
+ "gpt-5.6-sol": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 },
80
+ "gpt-5.6-terra": { input: 2, output: 12, cacheRead: 0.2, cacheWrite: 2.5 },
81
+ "gpt-5.6-luna": { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite: 0.25 },
82
+ "gpt-5.5": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
83
+ "gpt-5.4": { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 0 },
84
+ "gpt-5.3-codex": { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 0 },
85
+ "gpt-5.4-mini": { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },
86
+ "google/gemini-3.7-flash": { input: 0.75, output: 3.75, cacheRead: 0.075, cacheWrite: 0.04167 },
87
+ "google/gemini-3.6-flash": { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 0 },
88
+ "google/gemini-3.5-flash": { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 0 },
89
+ "google/gemini-3.5-flash-lite": { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0 },
90
+ "google/gemini-3.1-flash-lite": { input: 0.25, output: 1.5, cacheRead: 0.03, cacheWrite: 0 },
91
+ "sakana/fugu-ultra": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
92
+ "meta/muse-spark-1.1": { input: 1.25, output: 4.25, cacheRead: 0.15, cacheWrite: 0 },
93
+ "meta/muse-spark-1.2": { input: 1.25, output: 4.25, cacheRead: 0.15, cacheWrite: 0 },
94
+ "meta/muse-spark-1.2-contributor": { input: 0.1, output: 0.2, cacheRead: 0.002, cacheWrite: 0 },
95
+ "xai/grok-4.5": { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 },
96
+ "xai/grok-4.6": { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 },
97
+ };
98
+ export const MODEL_INPUT_MODALITIES = {
99
+ "MiniMaxAI/MiniMax-M3": ["text", "image"],
100
+ "Qwen/Qwen3.6-Plus": ["text", "image"],
101
+ "Qwen/Qwen3.7-Flash": ["text", "image"],
102
+ "Qwen/Qwen3.7-Plus": ["text", "image"],
103
+ "Qwen/Qwen3.8-27B": ["text", "image"],
104
+ "Qwen/Qwen3.8-Max": ["text", "image"],
105
+ "claude-fable-5": ["text", "image"],
106
+ "claude-haiku-4-5-20251001": ["text", "image"],
107
+ "claude-opus-4-7": ["text", "image"],
108
+ "claude-opus-4-8": ["text", "image"],
109
+ "claude-opus-5": ["text", "image"],
110
+ "claude-sonnet-4-6": ["text", "image"],
111
+ "claude-sonnet-5": ["text", "image"],
112
+ "google/gemini-3.1-flash-lite": ["text", "image"],
113
+ "google/gemini-3.5-flash": ["text", "image"],
114
+ "google/gemini-3.5-flash-lite": ["text", "image"],
115
+ "google/gemini-3.6-flash": ["text", "image"],
116
+ "google/gemini-3.7-flash": ["text", "image"],
117
+ "gpt-5.3-codex": ["text", "image"],
118
+ "gpt-5.4": ["text", "image"],
119
+ "gpt-5.4-mini": ["text", "image"],
120
+ "gpt-5.5": ["text", "image"],
121
+ "gpt-5.6-luna": ["text", "image"],
122
+ "gpt-5.6-sol": ["text", "image"],
123
+ "gpt-5.6-terra": ["text", "image"],
124
+ "meta/muse-spark-1.1": ["text", "image"],
125
+ "meta/muse-spark-1.2": ["text", "image"],
126
+ "meta/muse-spark-1.2-contributor": ["text", "image"],
127
+ "moonshotai/Kimi-K2.5": ["text", "image"],
128
+ "moonshotai/Kimi-K2.6": ["text", "image"],
129
+ "moonshotai/Kimi-K2.7-Code": ["text", "image"],
130
+ "moonshotai/Kimi-K2.7-Code-Highspeed": ["text", "image"],
131
+ "moonshotai/Kimi-K3": ["text", "image"],
132
+ "sakana/fugu-ultra": ["text", "image"],
133
+ "stepfun/Step-3.7-Flash": ["text", "image"],
134
+ "thinkingmachines/inkling": ["text", "image"],
135
+ "thinkingmachines/inkling-small": ["text", "image"],
136
+ "xai/grok-4.5": ["text", "image"],
137
+ "xiaomi/mimo-v2.5": ["text", "image"],
138
+ };
@@ -30,6 +30,11 @@ export declare function autoRegister(config: Config, snapshot: readonly CatalogM
30
30
  * Augments config-declared commandcode models with reasoning metadata and
31
31
  * variants so opencode's `ctrl+t` can cycle reasoning effort.
32
32
  *
33
+ * Gap-fill only: a user-declared `reasoning` value is never overwritten, and
34
+ * no variants are injected when the user explicitly disabled reasoning
35
+ * (variants without `reasoning: true` would make `ctrl+t` cycle an effort the
36
+ * model was told not to use).
37
+ *
33
38
  * In-place mutation of the config object (the plugin `config` hook contract).
34
39
  */
35
40
  export declare function augmentConfigCommandCodeModels(config: Config): void;
@@ -1,5 +1,5 @@
1
1
  import { MODEL_COSTS, ZERO_MODEL_COST } from "../provider/pricing.js";
2
- import { reasoningVariantsForModel } from "../provider/reasoning.js";
2
+ import { reasoningVariantsForModel, isReasoningModel } from "../provider/reasoning.js";
3
3
  import { inputModalitiesForModel } from "../provider/modalities.js";
4
4
  const DEFAULT_MAX_OUTPUT_TOKENS = 65_536;
5
5
  /**
@@ -47,6 +47,11 @@ export function autoRegister(config, snapshot, options) {
47
47
  * Augments config-declared commandcode models with reasoning metadata and
48
48
  * variants so opencode's `ctrl+t` can cycle reasoning effort.
49
49
  *
50
+ * Gap-fill only: a user-declared `reasoning` value is never overwritten, and
51
+ * no variants are injected when the user explicitly disabled reasoning
52
+ * (variants without `reasoning: true` would make `ctrl+t` cycle an effort the
53
+ * model was told not to use).
54
+ *
50
55
  * In-place mutation of the config object (the plugin `config` hook contract).
51
56
  */
52
57
  export function augmentConfigCommandCodeModels(config) {
@@ -56,9 +61,12 @@ export function augmentConfigCommandCodeModels(config) {
56
61
  for (const [modelId, model] of Object.entries(provider.models)) {
57
62
  if (!model)
58
63
  continue;
64
+ if (model.reasoning === undefined && isReasoningModel(modelId))
65
+ model.reasoning = true;
66
+ if (model.reasoning === false)
67
+ continue;
59
68
  const variants = reasoningVariantsForModel(modelId);
60
69
  if (variants) {
61
- model.reasoning = true;
62
70
  model.variants = variants;
63
71
  }
64
72
  }
@@ -78,8 +86,14 @@ function configModelFor(model) {
78
86
  context: model.contextLength,
79
87
  output: Math.min(model.contextLength, DEFAULT_MAX_OUTPUT_TOKENS),
80
88
  },
81
- reasoning: variants ? true : undefined,
89
+ reasoning: isReasoningModel(model.id) ? true : undefined,
82
90
  variants: variants,
91
+ // `tool_call: true` advertises tool use (the runtime already sends tools).
92
+ // `attachment` is deliberately unset: Command Code's published catalog
93
+ // exposes no per-model attachment support, and the runtime converter only
94
+ // handles text + image content parts — claiming attachment support would
95
+ // promise file uploads the plugin cannot deliver.
96
+ tool_call: true,
83
97
  modalities: {
84
98
  input: [...inputModalitiesForModel(model.id)],
85
99
  },
@@ -1,13 +1,5 @@
1
1
  export function calculateCommandCodeCost(model, usage) {
2
- const inputTokens = usage.input + usage.cacheRead + usage.cacheWrite;
3
- let rates = model.cost;
4
- let matchedThreshold = -1;
5
- for (const tier of model.cost.tiers ?? []) {
6
- if (inputTokens > tier.inputTokensAbove && tier.inputTokensAbove > matchedThreshold) {
7
- rates = tier;
8
- matchedThreshold = tier.inputTokensAbove;
9
- }
10
- }
2
+ const rates = model.cost;
11
3
  const longWrite = usage.cacheWrite1h ?? 0;
12
4
  const shortWrite = usage.cacheWrite - longWrite;
13
5
  usage.cost.input = (rates.input / 1_000_000) * usage.input;
@@ -1,9 +1,5 @@
1
+ import { MODEL_INPUT_MODALITIES } from "../catalog/facts.js";
1
2
  export type CommandCodeInputType = "text" | "image";
2
- /**
3
- * Model input modalities from the command-code@1.15.1 bundled catalog.
4
- * Models omitted here remain text-only so newly discovered IDs never claim
5
- * image support without upstream evidence.
6
- */
7
- export declare const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCodeInputType[]>>;
3
+ export { MODEL_INPUT_MODALITIES };
8
4
  export declare function inputModalitiesForModel(modelId: string): readonly CommandCodeInputType[];
9
5
  export declare function modelSupportsImageInput(modelId: string): boolean;
@@ -1,49 +1,7 @@
1
- // src/provider/modalities.ts — image input modality table (PLAN #5 Part B,
2
- // port of pi's models.ts:17-55)
3
- /**
4
- * Model input modalities from the command-code@1.15.1 bundled catalog.
5
- * Models omitted here remain text-only so newly discovered IDs never claim
6
- * image support without upstream evidence.
7
- */
8
- export const MODEL_INPUT_MODALITIES = {
9
- "MiniMaxAI/MiniMax-M3": ["text", "image"],
10
- "Qwen/Qwen3.6-Plus": ["text", "image"],
11
- "Qwen/Qwen3.7-Flash": ["text", "image"],
12
- "Qwen/Qwen3.7-Plus": ["text", "image"],
13
- "Qwen/Qwen3.8-Max": ["text", "image"],
14
- "claude-fable-5": ["text", "image"],
15
- "claude-haiku-4-5-20251001": ["text", "image"],
16
- "claude-opus-4-7": ["text", "image"],
17
- "claude-opus-4-8": ["text", "image"],
18
- "claude-opus-5": ["text", "image"],
19
- "claude-sonnet-4-6": ["text", "image"],
20
- "claude-sonnet-5": ["text", "image"],
21
- "google/gemini-3.1-flash-lite": ["text", "image"],
22
- "google/gemini-3.5-flash": ["text", "image"],
23
- "google/gemini-3.5-flash-lite": ["text", "image"],
24
- "google/gemini-3.6-flash": ["text", "image"],
25
- "gpt-5.3-codex": ["text", "image"],
26
- "gpt-5.4": ["text", "image"],
27
- "gpt-5.4-mini": ["text", "image"],
28
- "gpt-5.5": ["text", "image"],
29
- "gpt-5.6-luna": ["text", "image"],
30
- "gpt-5.6-sol": ["text", "image"],
31
- "gpt-5.6-terra": ["text", "image"],
32
- "meta/muse-spark-1.1": ["text", "image"],
33
- "meta/muse-spark-1.2": ["text", "image"],
34
- "meta/muse-spark-1.2-contributor": ["text", "image"],
35
- "moonshotai/Kimi-K2.5": ["text", "image"],
36
- "moonshotai/Kimi-K2.6": ["text", "image"],
37
- "moonshotai/Kimi-K2.7-Code": ["text", "image"],
38
- "moonshotai/Kimi-K2.7-Code-Highspeed": ["text", "image"],
39
- "moonshotai/Kimi-K3": ["text", "image"],
40
- "sakana/fugu-ultra": ["text", "image"],
41
- "stepfun/Step-3.7-Flash": ["text", "image"],
42
- "thinkingmachines/inkling": ["text", "image"],
43
- "thinkingmachines/inkling-small": ["text", "image"],
44
- "xai/grok-4.5": ["text", "image"],
45
- "xiaomi/mimo-v2.5": ["text", "image"],
46
- };
1
+ // src/provider/modalities.ts — image input modalities from generated catalog
2
+ // facts. Text-only models are intentionally omitted and use the fallback below.
3
+ import { MODEL_INPUT_MODALITIES } from "../catalog/facts.js";
4
+ export { MODEL_INPUT_MODALITIES };
47
5
  const TEXT_INPUT_ONLY = ["text"];
48
6
  export function inputModalitiesForModel(modelId) {
49
7
  return MODEL_INPUT_MODALITIES[modelId] ?? TEXT_INPUT_ONLY;
@@ -1,24 +1,11 @@
1
+ import { MODEL_COSTS, FACTS_SOURCE_URL as PRICING_SOURCE_URL, FACTS_LAST_REFRESHED as PRICING_LAST_VERIFIED } from "../catalog/facts.js";
2
+ export { MODEL_COSTS, PRICING_SOURCE_URL, PRICING_LAST_VERIFIED };
1
3
  export interface CommandCodeModelCostRates {
2
4
  input: number;
3
5
  output: number;
4
6
  cacheRead: number;
5
7
  cacheWrite: number;
6
8
  }
7
- export interface CommandCodeModelCostTier extends CommandCodeModelCostRates {
8
- inputTokensAbove: number;
9
- }
10
9
  export interface CommandCodeModelCost extends CommandCodeModelCostRates {
11
- tiers?: readonly CommandCodeModelCostTier[];
12
10
  }
13
- export declare const PRICING_SOURCE_URL = "https://commandcode.ai/docs/resources/pricing-limits";
14
- export declare const PRICING_LAST_VERIFIED = "2026-08-04";
15
11
  export declare const ZERO_MODEL_COST: CommandCodeModelCost;
16
- /**
17
- * Display prices in USD per million tokens.
18
- *
19
- * Context-dependent rates use pi's request-wide input pricing tiers. The
20
- * highest threshold exceeded by input + cache reads + cache writes applies to
21
- * the full request. The Command Code usage page remains authoritative for the
22
- * amount billed for an individual request.
23
- */
24
- export declare const MODEL_COSTS: Readonly<Record<string, CommandCodeModelCost>>;
@@ -1,188 +1,10 @@
1
- // src/provider/pricing.ts — Command Code model pricing table (PLAN #6, port of
2
- // pi's pricing.ts)
3
- export const PRICING_SOURCE_URL = "https://commandcode.ai/docs/resources/pricing-limits";
4
- export const PRICING_LAST_VERIFIED = "2026-08-04";
1
+ // src/provider/pricing.ts — Command Code model pricing types + cost table.
2
+ // MODEL_COSTS is GENERATED (src/catalog/facts.ts) — see FACTS_SOURCE_URL.
3
+ import { MODEL_COSTS, FACTS_SOURCE_URL as PRICING_SOURCE_URL, FACTS_LAST_REFRESHED as PRICING_LAST_VERIFIED, } from "../catalog/facts.js";
4
+ export { MODEL_COSTS, PRICING_SOURCE_URL, PRICING_LAST_VERIFIED };
5
5
  export const ZERO_MODEL_COST = {
6
6
  input: 0,
7
7
  output: 0,
8
8
  cacheRead: 0,
9
9
  cacheWrite: 0,
10
10
  };
11
- /**
12
- * Display prices in USD per million tokens.
13
- *
14
- * Context-dependent rates use pi's request-wide input pricing tiers. The
15
- * highest threshold exceeded by input + cache reads + cache writes applies to
16
- * the full request. The Command Code usage page remains authoritative for the
17
- * amount billed for an individual request.
18
- */
19
- export const MODEL_COSTS = {
20
- // Free models
21
- "poolside/laguna-s-2.1-free": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
22
- "inclusionai/ling-3.0-flash-free": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
23
- // Open and open-weight models
24
- "tencent/hy3-paid": { input: 0.14, output: 0.58, cacheRead: 0.035, cacheWrite: 0 },
25
- "moonshotai/Kimi-K3": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 0 },
26
- "moonshotai/Kimi-K2.7-Code": { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0 },
27
- "moonshotai/Kimi-K2.7-Code-Highspeed": {
28
- input: 1.9,
29
- output: 8,
30
- cacheRead: 0.38,
31
- cacheWrite: 0,
32
- },
33
- "moonshotai/Kimi-K2.6": { input: 0.95, output: 4, cacheRead: 0.16, cacheWrite: 0 },
34
- "moonshotai/Kimi-K2.5": { input: 0.6, output: 3, cacheRead: 0.1, cacheWrite: 0 },
35
- "zai-org/GLM-5.2": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
36
- "zai-org/GLM-5.2-Fast": { input: 3, output: 10.25, cacheRead: 0.5, cacheWrite: 0 },
37
- "zai-org/GLM-5.1": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
38
- "zai-org/GLM-5": { input: 1, output: 3.2, cacheRead: 0.2, cacheWrite: 0 },
39
- "MiniMaxAI/MiniMax-M3": { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0 },
40
- "MiniMaxAI/MiniMax-M2.7": { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0 },
41
- "MiniMaxAI/MiniMax-M2.5": { input: 0.3, output: 1.2, cacheRead: 0.03, cacheWrite: 0 },
42
- // Permanent 75% discount.
43
- "deepseek/deepseek-v4-pro": {
44
- input: 0.435,
45
- output: 0.87,
46
- cacheRead: 0.003625,
47
- cacheWrite: 0,
48
- },
49
- "deepseek/deepseek-v4-flash": {
50
- input: 0.14,
51
- output: 0.28,
52
- cacheRead: 0.0028,
53
- cacheWrite: 0,
54
- },
55
- "Qwen/Qwen3.8-Max": { input: 2, output: 6, cacheRead: 0.25, cacheWrite: 2.5 },
56
- "Qwen/Qwen3.7-Max": { input: 2.5, output: 7.5, cacheRead: 0.5, cacheWrite: 3.13 },
57
- "Qwen/Qwen3.7-Plus": {
58
- input: 0.4,
59
- output: 1.6,
60
- cacheRead: 0.08,
61
- cacheWrite: 0.5,
62
- tiers: [
63
- {
64
- inputTokensAbove: 256_000,
65
- input: 1.2,
66
- output: 4.8,
67
- cacheRead: 0.24,
68
- cacheWrite: 1.5,
69
- },
70
- ],
71
- },
72
- "Qwen/Qwen3.7-Flash": {
73
- input: 0.03,
74
- output: 0.13,
75
- cacheRead: 0.006,
76
- cacheWrite: 0.038,
77
- tiers: [
78
- {
79
- inputTokensAbove: 32_000,
80
- input: 0.1,
81
- output: 0.4,
82
- cacheRead: 0.02,
83
- cacheWrite: 0.125,
84
- },
85
- {
86
- inputTokensAbove: 256_000,
87
- input: 0.2,
88
- output: 0.8,
89
- cacheRead: 0.04,
90
- cacheWrite: 0.25,
91
- },
92
- ],
93
- },
94
- "Qwen/Qwen3.6-Max-Preview": {
95
- input: 1.3,
96
- output: 7.8,
97
- cacheRead: 0.26,
98
- cacheWrite: 1.63,
99
- },
100
- "Qwen/Qwen3.6-Plus": { input: 0.5, output: 3, cacheRead: 0.1, cacheWrite: 0 },
101
- "stepfun/Step-3.7-Flash": { input: 0.2, output: 1.15, cacheRead: 0.04, cacheWrite: 0 },
102
- "stepfun/Step-3.5-Flash": { input: 0.1, output: 0.3, cacheRead: 0.02, cacheWrite: 0 },
103
- // Permanent discounted rates.
104
- "xiaomi/mimo-v2.5-pro": { input: 0.435, output: 0.87, cacheRead: 0.0036, cacheWrite: 0 },
105
- "xiaomi/mimo-v2.5": { input: 0.14, output: 0.28, cacheRead: 0.0028, cacheWrite: 0 },
106
- "nvidia/nemotron-3-ultra-550b-a55b": {
107
- input: 0.6,
108
- output: 2.4,
109
- cacheRead: 0.12,
110
- cacheWrite: 0,
111
- },
112
- "sakana/fugu-ultra": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
113
- "thinkingmachines/inkling": { input: 1, output: 4.05, cacheRead: 0.17, cacheWrite: 0 },
114
- "thinkingmachines/inkling-small": {
115
- input: 0.5,
116
- output: 1.2,
117
- cacheRead: 0.1,
118
- cacheWrite: 0,
119
- },
120
- "meta/muse-spark-1.1": { input: 1.25, output: 4.25, cacheRead: 0.15, cacheWrite: 0 },
121
- // Anthropic
122
- // Introductory pricing through 2026-08-31.
123
- "claude-sonnet-5": { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 },
124
- "claude-sonnet-4-6": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
125
- "claude-fable-5": { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
126
- "claude-opus-5": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
127
- "claude-opus-4-8": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
128
- "claude-opus-4-7": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
129
- "claude-haiku-4-5-20251001": {
130
- input: 1,
131
- output: 5,
132
- cacheRead: 0.1,
133
- cacheWrite: 1.25,
134
- },
135
- // OpenAI
136
- "gpt-5.6-sol": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 },
137
- // Discounted rates through 2026-08-14.
138
- "gpt-5.6-terra": {
139
- input: 1,
140
- output: 6,
141
- cacheRead: 0.1,
142
- cacheWrite: 1.25,
143
- tiers: [
144
- {
145
- inputTokensAbove: 272_000,
146
- input: 2,
147
- output: 9,
148
- cacheRead: 0.2,
149
- cacheWrite: 2.5,
150
- },
151
- ],
152
- },
153
- "gpt-5.6-luna": {
154
- input: 0.1,
155
- output: 0.6,
156
- cacheRead: 0.01,
157
- cacheWrite: 0.125,
158
- tiers: [
159
- {
160
- inputTokensAbove: 272_000,
161
- input: 0.2,
162
- output: 0.9,
163
- cacheRead: 0.02,
164
- cacheWrite: 0.25,
165
- },
166
- ],
167
- },
168
- "gpt-5.5": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
169
- "gpt-5.4": { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 0 },
170
- "gpt-5.3-codex": { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 0 },
171
- "gpt-5.4-mini": { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },
172
- // Google and xAI
173
- "google/gemini-3.6-flash": { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 0 },
174
- "google/gemini-3.5-flash": { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 0 },
175
- "google/gemini-3.5-flash-lite": {
176
- input: 0.3,
177
- output: 2.5,
178
- cacheRead: 0.03,
179
- cacheWrite: 0,
180
- },
181
- "google/gemini-3.1-flash-lite": {
182
- input: 0.25,
183
- output: 1.5,
184
- cacheRead: 0.03,
185
- cacheWrite: 0,
186
- },
187
- "xai/grok-4.5": { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 },
188
- };
@@ -1,15 +1,15 @@
1
+ /**
2
+ * Models omitted here let Command Code choose their reasoning depth.
3
+ */
4
+ export declare const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>>;
1
5
  export type PiThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
2
6
  type CommandCodeReasoningEffort = Exclude<PiThinkingLevel, "off">;
3
7
  /**
4
- * Per-model reasoning efforts supported by Command Code's generate endpoint.
5
- *
6
- * The Provider API does not expose reasoning metadata. This is an exact
7
- * snapshot of `reasoningEfforts` from the command-code@1.15.1 model catalog
8
- * (`packages/shared/src/model-catalog.ts`, also published in the generated
9
- * `dist/bundled/command-code-knowledge/reference/models.md`). Models omitted
10
- * here let Command Code choose their reasoning depth, matching the CLI.
8
+ * Models Command Code advertises as reasoning-capable without exposing
9
+ * explicit effort levels (Command Code chooses the reasoning depth). These
10
+ * advertise `reasoning: true` in opencode but never generate `variants`.
11
11
  */
12
- export declare const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>>;
12
+ export declare const REASONING_MODELS: ReadonlySet<string>;
13
13
  export interface ThinkingMetadata {
14
14
  thinkingLevelMap: Partial<Record<PiThinkingLevel, string | null>>;
15
15
  thinking: {
@@ -1,39 +1,44 @@
1
1
  // src/provider/reasoning.ts — reasoning-effort metadata tables (PLAN #5 Part A,
2
- // port of pi's models.ts:67-150; catalog parsing deferred to #6/#7)
2
+ // port of pi's models.ts:67-150). Reasoning metadata now comes from the
3
+ // generated catalog facts (`src/catalog/facts.ts`), with hand-maintained
4
+ // classification sets (`REASONING_MODELS`) layered on top.
5
+ import { MODEL_EFFORTS as GENERATED_MODEL_EFFORTS } from "../catalog/facts.js";
3
6
  /**
4
- * Per-model reasoning efforts supported by Command Code's generate endpoint.
5
- *
6
- * The Provider API does not expose reasoning metadata. This is an exact
7
- * snapshot of `reasoningEfforts` from the command-code@1.15.1 model catalog
8
- * (`packages/shared/src/model-catalog.ts`, also published in the generated
9
- * `dist/bundled/command-code-knowledge/reference/models.md`). Models omitted
10
- * here let Command Code choose their reasoning depth, matching the CLI.
7
+ * Models omitted here let Command Code choose their reasoning depth.
11
8
  */
12
- export const MODEL_EFFORTS = {
13
- "Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"],
14
- "claude-fable-5": ["low", "medium", "high", "xhigh", "max"],
15
- "claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"],
16
- "claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"],
17
- "claude-opus-5": ["low", "medium", "high", "xhigh", "max"],
18
- "claude-sonnet-4-6": ["low", "medium", "high", "xhigh", "max"],
19
- "claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"],
20
- "deepseek/deepseek-v4-flash": ["high", "max"],
21
- "deepseek/deepseek-v4-pro": ["high", "max"],
22
- "gpt-5.3-codex": ["low", "medium", "high", "xhigh"],
23
- "gpt-5.4": ["low", "medium", "high", "xhigh"],
24
- "gpt-5.4-mini": ["low", "medium", "high"],
25
- "gpt-5.5": ["low", "medium", "high", "xhigh"],
26
- "gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"],
27
- "gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max"],
28
- "gpt-5.6-terra": ["low", "medium", "high", "xhigh", "max"],
29
- "google/gemini-3.1-flash-lite": ["low", "medium", "high"],
30
- "google/gemini-3.5-flash": ["low", "medium", "high"],
31
- "google/gemini-3.5-flash-lite": ["low", "medium", "high"],
32
- "google/gemini-3.6-flash": ["low", "medium", "high"],
33
- "sakana/fugu-ultra": ["high", "xhigh"],
34
- "xai/grok-4.5": ["low", "medium", "high"],
35
- "zai-org/GLM-5.2": ["high", "max"],
36
- };
9
+ // facts.ts types MODEL_EFFORTS values as `readonly string[]`, wider than the
10
+ // local `CommandCodeReasoningEffort` union ("minimal" | "low" | ... | "max"),
11
+ // so a literal `export { MODEL_EFFORTS }` fails typecheck where
12
+ // `ThinkingMetadata` uses the union. The cast assumes every effort string the
13
+ // generated catalog emits is a valid `PiThinkingLevel` (the models.md Efforts
14
+ // column is constrained to those levels; the release gate catches drift). Do
15
+ // NOT "simplify" this back to a direct re-export — it breaks the build.
16
+ export const MODEL_EFFORTS = GENERATED_MODEL_EFFORTS;
17
+ /**
18
+ * Models Command Code advertises as reasoning-capable without exposing
19
+ * explicit effort levels (Command Code chooses the reasoning depth). These
20
+ * advertise `reasoning: true` in opencode but never generate `variants`.
21
+ */
22
+ export const REASONING_MODELS = new Set([
23
+ "MiniMaxAI/MiniMax-M3",
24
+ "Qwen/Qwen3.6-Max-Preview",
25
+ "Qwen/Qwen3.6-Plus",
26
+ "Qwen/Qwen3.7-Flash",
27
+ "Qwen/Qwen3.7-Max",
28
+ "Qwen/Qwen3.7-Plus",
29
+ "meta/muse-spark-1.1",
30
+ "meta/muse-spark-1.2",
31
+ "meta/muse-spark-1.2-contributor",
32
+ "moonshotai/Kimi-K2.7-Code",
33
+ "moonshotai/Kimi-K2.7-Code-Highspeed",
34
+ "moonshotai/Kimi-K3",
35
+ "nvidia/nemotron-3-ultra-550b-a55b",
36
+ "poolside/laguna-s-2.1-free",
37
+ "stepfun/Step-3.7-Flash",
38
+ "tencent/hy3-paid",
39
+ "thinkingmachines/inkling",
40
+ "thinkingmachines/inkling-small",
41
+ ]);
37
42
  const PI_THINKING_LEVELS = [
38
43
  "off",
39
44
  "minimal",
@@ -63,7 +68,7 @@ export function thinkingMetadataForModel(modelId) {
63
68
  };
64
69
  }
65
70
  export function isReasoningModel(modelId) {
66
- return MODEL_EFFORTS[modelId] !== undefined;
71
+ return MODEL_EFFORTS[modelId] !== undefined || REASONING_MODELS.has(modelId);
67
72
  }
68
73
  /**
69
74
  * opencode model `variants` for the Command Code model: one entry per supported
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-cmd-provider",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "description": "Command Code provider + plugin for opencode",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,7 +19,7 @@
19
19
  "typecheck": "tsc --noEmit",
20
20
  "refresh:snapshot": "node scripts/refresh-snapshot.mjs",
21
21
  "test": "npm run typecheck && npm run test:unit && npm run test:integration && npm run test:contract && npm run format:check",
22
- "test:unit": "tsx tests/env.test.ts && tsx tests/auth-key.test.ts && tsx tests/converters.test.ts && tsx tests/stream.test.ts && tsx tests/redact.test.ts && tsx tests/cost.test.ts && tsx tests/retry.test.ts && tsx tests/reasoning.test.ts && tsx tests/modalities.test.ts && tsx tests/snapshot.test.ts && tsx tests/refresh-snapshot.test.ts && tsx tests/oauth.test.ts && tsx tests/plugin-models.test.ts",
22
+ "test:unit": "tsx tests/env.test.ts && tsx tests/auth-key.test.ts && tsx tests/converters.test.ts && tsx tests/stream.test.ts && tsx tests/redact.test.ts && tsx tests/cost.test.ts && tsx tests/retry.test.ts && tsx tests/reasoning.test.ts && tsx tests/modalities.test.ts && tsx tests/snapshot.test.ts && tsx tests/refresh-snapshot.test.ts && tsx tests/oauth.test.ts && tsx tests/parse-facts.test.ts && tsx tests/parse-modalities.test.ts && tsx tests/catalog-metadata.test.ts && tsx tests/plugin-models.test.ts",
23
23
  "test:integration": "tsx tests/integration-do-stream.test.ts && tsx tests/integration-do-generate.test.ts",
24
24
  "test:contract": "tsx tests/contract.test.ts",
25
25
  "test:e2e": "node tests/e2e-opencode.mjs",
@@ -44,6 +44,7 @@
44
44
  "devDependencies": {
45
45
  "@opencode-ai/plugin": "*",
46
46
  "@types/node": "^26.2.0",
47
+ "acorn": "^8.18.0",
47
48
  "prettier": "^3.9.6",
48
49
  "tsx": "^4.23.12",
49
50
  "typescript": "^7.0.2"