From 003d571b5e73e6eb4c48ef3c8d26cdec52d892b9 Mon Sep 17 00:00:00 2001 From: Patrick Wozniak Date: Tue, 25 Aug 2026 14:12:00 +0200 Subject: [PATCH 1/8] fix(models): refresh Command Code capabilities --- CHANGELOG.md | 1 + README.md | 2 +- src/core.ts | 2 +- src/models.ts | 14 +++++++++++--- tests/test-models.ts | 18 ++++++++++++++---- tests/test-stream.ts | 2 +- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f861b13..99640d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Refresh static model capabilities from `command-code@1.32.2`, including new image and reasoning metadata. - Add `/commandcode-quota` with live credits, plan, usage totals, and rolling-limit diagnostics from Command Code's alpha usage endpoints. - Add `zai-org/GLM-5.3` with its verified reasoning efforts and display pricing. - Prefer Command Code's Provider API (`/provider/v1/chat/completions` and `/provider/v1/messages`) and automatically fall back to the existing `/alpha/generate` transport only when the Provider API returns `403 upgrade_required` for a Go-plan account. diff --git a/README.md b/README.md index 6c757dc..6caf1d2 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ The following environment variables are intended for tests, local mocks, and com ## Image input -The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.1`; unknown models default to text-only until their upstream metadata is reviewed. +The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.2`; unknown models default to text-only until their upstream metadata is reviewed. For vision-capable models, Pi's native provider adapters forward image blocks from user messages and tool results using the documented OpenAI or Anthropic message schema. Unknown and text-only models remain marked text-only in Pi. diff --git a/src/core.ts b/src/core.ts index 2eb92f8..d2b95f8 100644 --- a/src/core.ts +++ b/src/core.ts @@ -43,7 +43,7 @@ export * from "./overflow.ts" export * from "./types.ts" export const DEFAULT_API_BASE = "https://api.commandcode.ai" -export const COMMAND_CODE_CLI_VERSION = "1.32.1" +export const COMMAND_CODE_CLI_VERSION = "1.32.2" const DEFAULT_GENERATE_MAX_TOKENS = 64_000 const DEFAULT_MAX_RETRIES = 0 diff --git a/src/models.ts b/src/models.ts index 706689d..0b30631 100644 --- a/src/models.ts +++ b/src/models.ts @@ -12,7 +12,7 @@ export type CommandCodeApi = "openai-completions" | "anthropic-messages" export type CommandCodeInputType = "text" | "image" /** - * Model input modalities from the command-code@1.32.1 bundled catalog. + * Model input modalities from the command-code@1.32.2 bundled catalog. * Models omitted here remain text-only so newly discovered IDs never claim * image support without upstream evidence. */ @@ -21,6 +21,7 @@ export const MODEL_INPUT_MODALITIES: Readonly * Per-model reasoning efforts supported by Command Code's generate endpoint. * * The Provider API does not expose reasoning metadata. This is an exact - * snapshot of `reasoningEfforts` from the command-code@1.32.1 model catalog + * snapshot of `reasoningEfforts` from the command-code@1.32.2 model catalog * (`packages/shared/src/model-catalog.ts`, also published in the generated * `dist/bundled/command-code-knowledge/reference/models.md`). Models omitted * here let Command Code choose their reasoning depth, matching the CLI. */ export const MODEL_EFFORTS: Readonly> = { + "Qwen/Qwen3.8-27B": ["low", "medium", "xhigh"], "Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"], "claude-fable-5": ["low", "medium", "high", "xhigh", "max"], "claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"], @@ -89,6 +93,7 @@ export const MODEL_EFFORTS: Readonly { ) }) - it("matches command-code@1.32.1 image input capabilities", () => { + it("matches command-code@1.32.2 image input capabilities", () => { assert.deepEqual(inputModalitiesForModel("gpt-5.6-luna"), ["text", "image"]) assert.deepEqual(inputModalitiesForModel("meta/muse-spark-1.2"), ["text", "image"]) assert.deepEqual(inputModalitiesForModel("deepseek/deepseek-v4-flash-vision-exp"), [ "text", "image", ]) + assert.deepEqual(inputModalitiesForModel("Qwen/Qwen3.8-27B"), ["text", "image"]) + assert.deepEqual(inputModalitiesForModel("google/gemini-3.7-flash"), ["text", "image"]) + assert.deepEqual(inputModalitiesForModel("stealth/ox-alpha"), ["text", "image"]) assert.deepEqual(inputModalitiesForModel("deepseek/deepseek-v4-pro"), ["text"]) + assert.deepEqual(inputModalitiesForModel("zai-org/GLM-5.3"), ["text"]) assert.deepEqual(inputModalitiesForModel("unknown-new-model"), ["text"]) assert.equal(modelSupportsImageInput("gpt-5.6-luna"), true) assert.equal(modelSupportsImageInput("deepseek/deepseek-v4-flash-vision-exp"), true) + assert.equal(modelSupportsImageInput("stealth/ox-alpha"), true) assert.equal(modelSupportsImageInput("deepseek/deepseek-v4-pro"), false) - assert.equal(Object.keys(MODEL_INPUT_MODALITIES).length, 38) + assert.equal(Object.keys(MODEL_INPUT_MODALITIES).length, 41) }) it("marks only known reasoning models as reasoning-capable", () => { @@ -128,8 +133,9 @@ describe("commandCodeModelsFromApiResponse()", () => { assert.equal(models[1]?.reasoning, false) }) - it("matches the exact command-code@1.32.1 reasoning effort catalog", () => { + it("matches the exact command-code@1.32.2 reasoning effort catalog", () => { assert.deepEqual(MODEL_EFFORTS, { + "Qwen/Qwen3.8-27B": ["low", "medium", "xhigh"], "Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"], "claude-fable-5": ["low", "medium", "high", "xhigh", "max"], "claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"], @@ -138,6 +144,7 @@ describe("commandCodeModelsFromApiResponse()", () => { "claude-sonnet-4-6": ["low", "medium", "high", "xhigh", "max"], "claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"], "deepseek/deepseek-v4-flash": ["high", "max"], + "deepseek/deepseek-v4-flash-vision-exp": ["high", "max"], "deepseek/deepseek-v4-pro": ["high", "max"], "gpt-5.3-codex": ["low", "medium", "high", "xhigh"], "gpt-5.4": ["low", "medium", "high", "xhigh"], @@ -150,10 +157,13 @@ describe("commandCodeModelsFromApiResponse()", () => { "google/gemini-3.5-flash": ["low", "medium", "high"], "google/gemini-3.5-flash-lite": ["low", "medium", "high"], "google/gemini-3.6-flash": ["low", "medium", "high"], + "google/gemini-3.7-flash": ["low", "medium", "high"], "sakana/fugu-ultra": ["high", "xhigh"], + "stealth/ox-alpha": ["low", "high", "max"], "xai/grok-4.5": ["low", "medium", "high"], - "zai-org/GLM-5.3": ["low", "high", "max"], + "xai/grok-4.6": ["low", "medium", "high", "xhigh"], "zai-org/GLM-5.2": ["high", "max"], + "zai-org/GLM-5.3": ["low", "high", "max"], }) }) diff --git a/tests/test-stream.ts b/tests/test-stream.ts index f831dc2..6346416 100644 --- a/tests/test-stream.ts +++ b/tests/test-stream.ts @@ -569,7 +569,7 @@ describe("streamCommandCode — request serialization", () => { const headers = server.lastRequestHeaders() assert.equal(headers.authorization, "Bearer mock-key") - assert.equal(headers["x-command-code-version"], "1.32.1") + assert.equal(headers["x-command-code-version"], "1.32.2") assert.equal(headers["x-project-slug"], "repo") assert.equal(headers["x-taste-learning"], "true") assert.equal(headers["x-co-flag"], "false") From f23863d326be864dd2682f8db6f87deb5148af7a Mon Sep 17 00:00:00 2001 From: Patrick Wozniak Date: Tue, 25 Aug 2026 14:14:44 +0200 Subject: [PATCH 2/8] ci(models): check Command Code metadata daily --- .../check-commandcode-model-metadata.ts | 327 ++++++++++++++++++ .github/workflows/model-metadata.yml | 28 ++ CHANGELOG.md | 1 + README.md | 2 +- package.json | 7 +- tests/test-model-metadata-check.ts | 88 +++++ tsconfig.json | 2 +- 7 files changed, 450 insertions(+), 5 deletions(-) create mode 100644 .github/scripts/check-commandcode-model-metadata.ts create mode 100644 .github/workflows/model-metadata.yml create mode 100644 tests/test-model-metadata-check.ts diff --git a/.github/scripts/check-commandcode-model-metadata.ts b/.github/scripts/check-commandcode-model-metadata.ts new file mode 100644 index 0000000..7182cc5 --- /dev/null +++ b/.github/scripts/check-commandcode-model-metadata.ts @@ -0,0 +1,327 @@ +import { execFile } from "node:child_process" +import { appendFile, mkdtemp, readFile, rm } from "node:fs/promises" +import { tmpdir } from "node:os" +import { join, resolve } from "node:path" +import { pathToFileURL } from "node:url" +import { promisify } from "node:util" + +import { COMMAND_CODE_CLI_VERSION } from "../../src/core.ts" +import { MODEL_EFFORTS, MODEL_INPUT_MODALITIES } from "../../src/models.ts" + +const execFileAsync = promisify(execFile) +const MODELS_REFERENCE_PATH = "dist/bundled/command-code-knowledge/reference/models.md" +const CLI_BUNDLE_PATH = "dist/cli.mjs" +const TEXT_ONLY_MARKER = ',__name(isKnownTextOnlyModel,"isKnownTextOnlyModel")' +const VALID_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]) + +export interface CommandCodeModelMetadata { + imageModelIds: readonly string[] + reasoningEfforts: Readonly> +} + +export interface ModelMetadataDiff { + addedImageModelIds: readonly string[] + removedImageModelIds: readonly string[] + addedReasoningModelIds: readonly string[] + removedReasoningModelIds: readonly string[] + changedReasoningModelIds: readonly string[] +} + +interface PackedPackage { + filename: string +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value) +} + +function isStringArray(value: unknown): value is string[] { + return Array.isArray(value) && value.every((entry) => typeof entry === "string") +} + +function sorted(values: Iterable): string[] { + return [...values].sort((left, right) => left.localeCompare(right)) +} + +function parsePackedPackage(value: unknown): PackedPackage { + if (!Array.isArray(value) || value.length !== 1 || !isRecord(value[0])) { + throw new Error("Expected npm pack to return one package") + } + + const filename = value[0].filename + if (typeof filename !== "string" || filename.length === 0) { + throw new Error("Expected npm pack to return a tarball filename") + } + + return { filename } +} + +export function parsePackageVersion(value: unknown): string { + if (typeof value !== "string" || !/^\d+\.\d+\.\d+(?:[-+].+)?$/.test(value)) { + throw new Error("Expected npm view to return one semantic version") + } + return value +} + +export function parseModelsReference(markdown: string): { + modelIds: readonly string[] + reasoningEfforts: Readonly> +} { + const modelIds = new Set() + const reasoningEfforts: Record = {} + + for (const line of markdown.split("\n")) { + const match = /^\| `([^`]+)` \| [^|]* \| [^|]* \| ([^|]*) \|/.exec(line) + if (!match) continue + + const modelId = match[1] + const effortsColumn = match[2]?.trim() + if (!modelId || !effortsColumn) throw new Error(`Could not parse model row: ${line}`) + if (modelIds.has(modelId)) throw new Error(`Duplicate model id in reference: ${modelId}`) + modelIds.add(modelId) + + if (effortsColumn === "—") continue + + const efforts = effortsColumn.split(",").map((effort) => effort.trim()) + if (efforts.length === 0 || efforts.some((effort) => !VALID_EFFORTS.has(effort))) { + throw new Error(`Unexpected reasoning efforts for ${modelId}: ${effortsColumn}`) + } + reasoningEfforts[modelId] = efforts + } + + if (modelIds.size === 0) throw new Error("No model rows found in Command Code reference") + + return { + modelIds: sorted(modelIds), + reasoningEfforts: Object.fromEntries( + Object.entries(reasoningEfforts).sort(([left], [right]) => left.localeCompare(right)), + ), + } +} + +export function parseKnownTextOnlyModelIds(bundle: string): readonly string[] { + const markerIndex = bundle.indexOf(TEXT_ONLY_MARKER) + if (markerIndex < 0) { + throw new Error("Could not find Command Code's isKnownTextOnlyModel catalog") + } + + const setStart = bundle.lastIndexOf("new Set([", markerIndex) + if (setStart < 0) throw new Error("Could not find the text-only model set") + + const arrayStart = setStart + "new Set(".length + const arrayEnd = markerIndex - 1 + const literal = bundle.slice(arrayStart, arrayEnd) + const parsed: unknown = JSON.parse(literal) + if (!isStringArray(parsed)) throw new Error("Expected the text-only model catalog to be strings") + + return sorted(new Set(parsed)) +} + +export function commandCodeModelMetadataFromContents( + modelsReference: string, + cliBundle: string, +): CommandCodeModelMetadata { + const reference = parseModelsReference(modelsReference) + const textOnlyModelIds = new Set(parseKnownTextOnlyModelIds(cliBundle)) + + return { + imageModelIds: reference.modelIds.filter((modelId) => !textOnlyModelIds.has(modelId)), + reasoningEfforts: reference.reasoningEfforts, + } +} + +export function currentModelMetadata(): CommandCodeModelMetadata { + return { + imageModelIds: sorted(Object.keys(MODEL_INPUT_MODALITIES)), + reasoningEfforts: Object.fromEntries( + Object.entries(MODEL_EFFORTS) + .sort(([left], [right]) => left.localeCompare(right)) + .map(([modelId, efforts]) => [modelId, [...efforts]]), + ), + } +} + +export function diffModelMetadata( + current: CommandCodeModelMetadata, + upstream: CommandCodeModelMetadata, +): ModelMetadataDiff { + const currentImages = new Set(current.imageModelIds) + const upstreamImages = new Set(upstream.imageModelIds) + const currentReasoningIds = Object.keys(current.reasoningEfforts) + const upstreamReasoningIds = Object.keys(upstream.reasoningEfforts) + const currentReasoningSet = new Set(currentReasoningIds) + const upstreamReasoningSet = new Set(upstreamReasoningIds) + + return { + addedImageModelIds: sorted( + upstream.imageModelIds.filter((modelId) => !currentImages.has(modelId)), + ), + removedImageModelIds: sorted( + current.imageModelIds.filter((modelId) => !upstreamImages.has(modelId)), + ), + addedReasoningModelIds: sorted( + upstreamReasoningIds.filter((modelId) => !currentReasoningSet.has(modelId)), + ), + removedReasoningModelIds: sorted( + currentReasoningIds.filter((modelId) => !upstreamReasoningSet.has(modelId)), + ), + changedReasoningModelIds: sorted( + upstreamReasoningIds.filter( + (modelId) => + currentReasoningSet.has(modelId) && + JSON.stringify(current.reasoningEfforts[modelId]) !== + JSON.stringify(upstream.reasoningEfforts[modelId]), + ), + ), + } +} + +export function hasModelMetadataDiff(diff: ModelMetadataDiff): boolean { + return Object.values(diff).some((modelIds) => modelIds.length > 0) +} + +function formatList(modelIds: readonly string[]): string { + return modelIds.length > 0 ? modelIds.map((modelId) => `\`${modelId}\``).join(", ") : "None" +} + +function formatReasoningChanges( + modelIds: readonly string[], + current: CommandCodeModelMetadata, + upstream: CommandCodeModelMetadata, +): string { + if (modelIds.length === 0) return "None" + return modelIds + .map( + (modelId) => + `\`${modelId}\`: \`${(current.reasoningEfforts[modelId] ?? []).join(", ")}\` → \`${( + upstream.reasoningEfforts[modelId] ?? [] + ).join(", ")}\``, + ) + .join("
") +} + +function metadataReport( + packageVersion: string, + current: CommandCodeModelMetadata, + upstream: CommandCodeModelMetadata, + diff: ModelMetadataDiff, +): string { + const status = hasModelMetadataDiff(diff) ? "❌ Drift detected" : "✅ Metadata is current" + return [ + "## Command Code static model metadata", + "", + `**${status}**`, + "", + `- Repository snapshot: \`command-code@${COMMAND_CODE_CLI_VERSION}\``, + `- Inspected package: \`command-code@${packageVersion}\``, + `- Image-capable models: ${current.imageModelIds.length} repository / ${upstream.imageModelIds.length} upstream`, + `- Reasoning models: ${Object.keys(current.reasoningEfforts).length} repository / ${Object.keys(upstream.reasoningEfforts).length} upstream`, + "", + "| Change | Models |", + "| --- | --- |", + `| New image support | ${formatList(diff.addedImageModelIds)} |`, + `| Removed image support | ${formatList(diff.removedImageModelIds)} |`, + `| New reasoning metadata | ${formatList(diff.addedReasoningModelIds)} |`, + `| Removed reasoning metadata | ${formatList(diff.removedReasoningModelIds)} |`, + `| Changed reasoning efforts | ${formatReasoningChanges(diff.changedReasoningModelIds, current, upstream)} |`, + "", + ].join("\n") +} + +async function resolvePackageSpec( + packageSpec: string, + directory: string, + npmCacheDirectory: string, +): Promise { + if (packageSpec !== "command-code@latest") return packageSpec + + const { stdout } = await execFileAsync( + "npm", + ["view", packageSpec, "version", "--json", "--prefer-online", "--cache", npmCacheDirectory], + { + cwd: directory, + encoding: "utf-8", + }, + ) + return `command-code@${parsePackageVersion(JSON.parse(stdout) as unknown)}` +} + +async function inspectPackedPackage(packageSpec: string): Promise<{ + packageVersion: string + metadata: CommandCodeModelMetadata +}> { + const directory = await mkdtemp(join(tmpdir(), "pi-commandcode-model-check-")) + const npmCacheDirectory = join(directory, "npm-cache") + + try { + const resolvedPackageSpec = await resolvePackageSpec(packageSpec, directory, npmCacheDirectory) + const { stdout } = await execFileAsync( + "npm", + ["pack", resolvedPackageSpec, "--json", "--prefer-online", "--cache", npmCacheDirectory], + { + cwd: directory, + encoding: "utf-8", + maxBuffer: 10 * 1024 * 1024, + }, + ) + const packed = parsePackedPackage(JSON.parse(stdout) as unknown) + await execFileAsync("tar", ["-xzf", packed.filename], { cwd: directory }) + + const packageDirectory = join(directory, "package") + const packageJsonContents = await readFile(join(packageDirectory, "package.json"), "utf-8") + const packageJson: unknown = JSON.parse(packageJsonContents) + if (!isRecord(packageJson) || typeof packageJson.version !== "string") { + throw new Error("Expected command-code package.json to contain a version") + } + + const [modelsReference, cliBundle] = await Promise.all([ + readFile(join(packageDirectory, MODELS_REFERENCE_PATH), "utf-8"), + readFile(join(packageDirectory, CLI_BUNDLE_PATH), "utf-8"), + ]) + + return { + packageVersion: packageJson.version, + metadata: commandCodeModelMetadataFromContents(modelsReference, cliBundle), + } + } finally { + await rm(directory, { recursive: true, force: true }) + } +} + +async function main(): Promise { + const packageSpec = process.argv[2] ?? "command-code@latest" + const current = currentModelMetadata() + const upstreamPackage = await inspectPackedPackage(packageSpec) + const diff = diffModelMetadata(current, upstreamPackage.metadata) + const report = metadataReport( + upstreamPackage.packageVersion, + current, + upstreamPackage.metadata, + diff, + ) + + console.log(report) + + const summaryPath = process.env.GITHUB_STEP_SUMMARY + if (summaryPath) await appendFile(summaryPath, report, "utf-8") + + if (hasModelMetadataDiff(diff)) { + throw new Error( + `Static model metadata differs from command-code@${upstreamPackage.packageVersion}. Update src/models.ts and the snapshot version.`, + ) + } +} + +function isMainModule(): boolean { + const entrypoint = process.argv[1] + return entrypoint !== undefined && pathToFileURL(resolve(entrypoint)).href === import.meta.url +} + +if (isMainModule()) { + try { + await main() + } catch (error) { + console.error(error instanceof Error ? error.message : String(error)) + process.exitCode = 1 + } +} diff --git a/.github/workflows/model-metadata.yml b/.github/workflows/model-metadata.yml new file mode 100644 index 0000000..c933905 --- /dev/null +++ b/.github/workflows/model-metadata.yml @@ -0,0 +1,28 @@ +name: Command Code model metadata + +on: + schedule: + - cron: "17 6 * * *" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: commandcode-model-metadata + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + registry-url: https://registry.npmjs.org + - run: npm ci + - name: Compare static metadata with the latest Command Code CLI + run: npm run check:model-metadata diff --git a/CHANGELOG.md b/CHANGELOG.md index 99640d1..c4e2930 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Add a daily GitHub Actions check that compares static image and reasoning metadata with the latest published Command Code CLI catalog. - Refresh static model capabilities from `command-code@1.32.2`, including new image and reasoning metadata. - Add `/commandcode-quota` with live credits, plan, usage totals, and rolling-limit diagnostics from Command Code's alpha usage endpoints. - Add `zai-org/GLM-5.3` with its verified reasoning efforts and display pricing. diff --git a/README.md b/README.md index 6caf1d2..7657981 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ The following environment variables are intended for tests, local mocks, and com ## Image input -The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.2`; unknown models default to text-only until their upstream metadata is reviewed. +The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.2`; unknown models default to text-only until their upstream metadata is reviewed. A daily GitHub Actions check compares the static image and reasoning metadata with the latest published CLI package and reports any drift. For vision-capable models, Pi's native provider adapters forward image blocks from user messages and tool results using the documented OpenAI or Anthropic message schema. Unknown and text-only models remain marked text-only in Pi. diff --git a/package.json b/package.json index 6e07011..8d14353 100644 --- a/package.json +++ b/package.json @@ -29,16 +29,17 @@ "LICENSE" ], "scripts": { - "test": "npm run typecheck && tsx tests/test-package-manifest.ts && tsx tests/test-api-key.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-quota.ts && tsx tests/test-quota-command.ts && tsx tests/test-retry.ts && tsx tests/test-transport.ts && node tests/test-pi-isolated.mjs && node tests/test-pi-authenticated.mjs && node tests/test-pi-local.mjs && node tests/test-omp-compat.mjs", + "test": "npm run typecheck && tsx tests/test-package-manifest.ts && tsx tests/test-api-key.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-model-metadata-check.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-quota.ts && tsx tests/test-quota-command.ts && tsx tests/test-retry.ts && tsx tests/test-transport.ts && node tests/test-pi-isolated.mjs && node tests/test-pi-authenticated.mjs && node tests/test-pi-local.mjs && node tests/test-omp-compat.mjs", "typecheck": "tsc --noEmit", "format:check": "prettier --check '**/*.{ts,mjs,json,md}'", "format": "prettier --write '**/*.{ts,mjs,json,md}'", "pi:isolated": "node scripts/pi-isolated.mjs", "pi:authenticated": "node scripts/pi-authenticated.mjs", + "check:model-metadata": "tsx .github/scripts/check-commandcode-model-metadata.ts command-code@latest", "test:quota": "tsx tests/test-quota.ts && tsx tests/test-quota-command.ts", - "test:unit": "tsx tests/test-api-key.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-quota.ts && tsx tests/test-quota-command.ts && tsx tests/test-retry.ts && tsx tests/test-transport.ts", + "test:unit": "tsx tests/test-api-key.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-model-metadata-check.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-quota.ts && tsx tests/test-quota-command.ts && tsx tests/test-retry.ts && tsx tests/test-transport.ts", "test:api-key": "tsx tests/test-api-key.ts", - "test:models": "tsx tests/test-models.ts", + "test:models": "tsx tests/test-models.ts && tsx tests/test-model-metadata-check.ts", "test:runtime": "tsx tests/test-runtime.ts", "test:pricing": "tsx tests/test-pricing.ts", "test:oauth": "tsx tests/test-oauth.ts", diff --git a/tests/test-model-metadata-check.ts b/tests/test-model-metadata-check.ts new file mode 100644 index 0000000..145b298 --- /dev/null +++ b/tests/test-model-metadata-check.ts @@ -0,0 +1,88 @@ +import assert from "node:assert/strict" +import { describe, it } from "node:test" + +import { + commandCodeModelMetadataFromContents, + diffModelMetadata, + hasModelMetadataDiff, + parseKnownTextOnlyModelIds, + parseModelsReference, + parsePackageVersion, + type CommandCodeModelMetadata, +} from "../.github/scripts/check-commandcode-model-metadata.ts" + +const MODELS_REFERENCE = ` +| Id (use EXACTLY this) | Name | Context | Efforts | $/1M in/out · cache read | Min plan | Best for | +|---|---|---|---|---|---|---| +| \`vision-model\` | Vision | 1M | low, high | $1/$2 | Go | images | +| \`text-model\` | Text | 200K | — | $1/$2 | Go | text | +` + +const CLI_BUNDLE = + 'const catalog=new Set(["text-model"]),__name(isKnownTextOnlyModel,"isKnownTextOnlyModel")' + +describe("Command Code model metadata checker", () => { + it("parses model ids and reasoning efforts from the generated reference", () => { + assert.deepEqual(parseModelsReference(MODELS_REFERENCE), { + modelIds: ["text-model", "vision-model"], + reasoningEfforts: { "vision-model": ["low", "high"] }, + }) + }) + + it("extracts the text-only set from the bundled CLI catalog", () => { + assert.deepEqual(parseKnownTextOnlyModelIds(CLI_BUNDLE), ["text-model"]) + }) + + it("accepts one exact npm registry version and rejects stale-looking output shapes", () => { + assert.equal(parsePackageVersion("1.32.2"), "1.32.2") + assert.equal(parsePackageVersion("2.0.0-beta.1"), "2.0.0-beta.1") + assert.throws(() => parsePackageVersion(["1.32.1", "1.32.2"]), /one semantic version/) + assert.throws(() => parsePackageVersion("latest"), /one semantic version/) + }) + + it("derives image support by excluding known text-only models", () => { + assert.deepEqual(commandCodeModelMetadataFromContents(MODELS_REFERENCE, CLI_BUNDLE), { + imageModelIds: ["vision-model"], + reasoningEfforts: { "vision-model": ["low", "high"] }, + }) + }) + + it("reports additions, removals, and changed reasoning efforts", () => { + const current: CommandCodeModelMetadata = { + imageModelIds: ["removed-image", "stable-image"], + reasoningEfforts: { + "changed-reasoning": ["low"], + "removed-reasoning": ["high"], + "stable-reasoning": ["low", "high"], + }, + } + const upstream: CommandCodeModelMetadata = { + imageModelIds: ["added-image", "stable-image"], + reasoningEfforts: { + "added-reasoning": ["max"], + "changed-reasoning": ["low", "high"], + "stable-reasoning": ["low", "high"], + }, + } + + const diff = diffModelMetadata(current, upstream) + + assert.deepEqual(diff, { + addedImageModelIds: ["added-image"], + removedImageModelIds: ["removed-image"], + addedReasoningModelIds: ["added-reasoning"], + removedReasoningModelIds: ["removed-reasoning"], + changedReasoningModelIds: ["changed-reasoning"], + }) + assert.equal(hasModelMetadataDiff(diff), true) + }) + + it("rejects unexpected upstream structures instead of silently passing", () => { + assert.throws(() => parseModelsReference("# no catalog"), /No model rows/) + assert.throws( + () => parseModelsReference(MODELS_REFERENCE.replace("low, high", "low, turbo")), + /Unexpected reasoning efforts/, + ) + assert.throws(() => parseKnownTextOnlyModelIds("const unrelated = true"), /Could not find/) + }) +}) diff --git a/tsconfig.json b/tsconfig.json index c691796..2cf50c2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,5 +9,5 @@ "strict": true, "types": ["node"] }, - "include": ["src/**/*.ts", "tests/**/*.ts"] + "include": [".github/scripts/**/*.ts", "src/**/*.ts", "tests/**/*.ts"] } From 7ed38d5f70cc864b111925082d66dfb525552ba2 Mon Sep 17 00:00:00 2001 From: Patrick Wozniak Date: Tue, 25 Aug 2026 14:43:46 +0200 Subject: [PATCH 3/8] ci(models): run metadata check on pull requests --- .github/scripts/check-commandcode-model-metadata.ts | 5 +---- .github/workflows/model-metadata.yml | 13 ++++++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/scripts/check-commandcode-model-metadata.ts b/.github/scripts/check-commandcode-model-metadata.ts index 7182cc5..bfc72ab 100644 --- a/.github/scripts/check-commandcode-model-metadata.ts +++ b/.github/scripts/check-commandcode-model-metadata.ts @@ -1,5 +1,5 @@ import { execFile } from "node:child_process" -import { appendFile, mkdtemp, readFile, rm } from "node:fs/promises" +import { mkdtemp, readFile, rm } from "node:fs/promises" import { tmpdir } from "node:os" import { join, resolve } from "node:path" import { pathToFileURL } from "node:url" @@ -302,9 +302,6 @@ async function main(): Promise { console.log(report) - const summaryPath = process.env.GITHUB_STEP_SUMMARY - if (summaryPath) await appendFile(summaryPath, report, "utf-8") - if (hasModelMetadataDiff(diff)) { throw new Error( `Static model metadata differs from command-code@${upstreamPackage.packageVersion}. Update src/models.ts and the snapshot version.`, diff --git a/.github/workflows/model-metadata.yml b/.github/workflows/model-metadata.yml index c933905..809eef8 100644 --- a/.github/workflows/model-metadata.yml +++ b/.github/workflows/model-metadata.yml @@ -1,6 +1,14 @@ name: Command Code model metadata on: + pull_request: + branches: [main] + paths: + - ".github/scripts/check-commandcode-model-metadata.ts" + - ".github/workflows/model-metadata.yml" + - "src/core.ts" + - "src/models.ts" + - "tests/test-model-metadata-check.ts" schedule: - cron: "17 6 * * *" workflow_dispatch: @@ -25,4 +33,7 @@ jobs: registry-url: https://registry.npmjs.org - run: npm ci - name: Compare static metadata with the latest Command Code CLI - run: npm run check:model-metadata + run: npm run check:model-metadata | tee model-metadata-report.md + - name: Publish metadata report + if: always() + run: cat model-metadata-report.md >> "$GITHUB_STEP_SUMMARY" From af5c41aaa187bf4be678c236280f0d70f05250f6 Mon Sep 17 00:00:00 2001 From: Patrick Wozniak Date: Tue, 25 Aug 2026 14:48:03 +0200 Subject: [PATCH 4/8] ci(models): limit metadata check to scheduled runs --- .github/workflows/model-metadata.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/model-metadata.yml b/.github/workflows/model-metadata.yml index 809eef8..de26c03 100644 --- a/.github/workflows/model-metadata.yml +++ b/.github/workflows/model-metadata.yml @@ -1,14 +1,6 @@ name: Command Code model metadata on: - pull_request: - branches: [main] - paths: - - ".github/scripts/check-commandcode-model-metadata.ts" - - ".github/workflows/model-metadata.yml" - - "src/core.ts" - - "src/models.ts" - - "tests/test-model-metadata-check.ts" schedule: - cron: "17 6 * * *" workflow_dispatch: From ef5d723182db3294bb00e3d441bed315f2f9b98a Mon Sep 17 00:00:00 2001 From: Patrick Wozniak Date: Tue, 25 Aug 2026 14:48:29 +0200 Subject: [PATCH 5/8] Revert "ci(models): limit metadata check to scheduled runs" This reverts commit af5c41aaa187bf4be678c236280f0d70f05250f6. --- .github/workflows/model-metadata.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/model-metadata.yml b/.github/workflows/model-metadata.yml index de26c03..809eef8 100644 --- a/.github/workflows/model-metadata.yml +++ b/.github/workflows/model-metadata.yml @@ -1,6 +1,14 @@ name: Command Code model metadata on: + pull_request: + branches: [main] + paths: + - ".github/scripts/check-commandcode-model-metadata.ts" + - ".github/workflows/model-metadata.yml" + - "src/core.ts" + - "src/models.ts" + - "tests/test-model-metadata-check.ts" schedule: - cron: "17 6 * * *" workflow_dispatch: From 3d8758fe3b65472651384f116a23a36727012462 Mon Sep 17 00:00:00 2001 From: Patrick Wozniak Date: Tue, 25 Aug 2026 15:08:22 +0200 Subject: [PATCH 6/8] feat(models): generate Command Code catalog snapshot --- .../check-commandcode-model-metadata.ts | 101 +++++++++++++++-- package.json | 3 +- src/commandcode-catalog.ts | 84 ++++++++++++++ src/core.ts | 3 +- src/models.ts | 103 ++---------------- tests/test-model-metadata-check.ts | 61 +++++++++++ tests/test-models.ts | 48 +++----- tests/test-stream.ts | 3 +- 8 files changed, 269 insertions(+), 137 deletions(-) create mode 100644 src/commandcode-catalog.ts diff --git a/.github/scripts/check-commandcode-model-metadata.ts b/.github/scripts/check-commandcode-model-metadata.ts index bfc72ab..4cfa4e2 100644 --- a/.github/scripts/check-commandcode-model-metadata.ts +++ b/.github/scripts/check-commandcode-model-metadata.ts @@ -1,18 +1,24 @@ import { execFile } from "node:child_process" -import { mkdtemp, readFile, rm } from "node:fs/promises" +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises" import { tmpdir } from "node:os" import { join, resolve } from "node:path" import { pathToFileURL } from "node:url" import { promisify } from "node:util" -import { COMMAND_CODE_CLI_VERSION } from "../../src/core.ts" -import { MODEL_EFFORTS, MODEL_INPUT_MODALITIES } from "../../src/models.ts" +import { + COMMAND_CODE_CLI_VERSION, + MODEL_EFFORTS, + MODEL_INPUT_MODALITIES, +} from "../../src/commandcode-catalog.ts" const execFileAsync = promisify(execFile) const MODELS_REFERENCE_PATH = "dist/bundled/command-code-knowledge/reference/models.md" const CLI_BUNDLE_PATH = "dist/cli.mjs" const TEXT_ONLY_MARKER = ',__name(isKnownTextOnlyModel,"isKnownTextOnlyModel")' -const VALID_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]) +const VALID_EFFORTS = new Set(["minimal", "low", "medium", "high", "xhigh", "max"]) +const CATALOG_SOURCE_PATH = new URL("../../src/commandcode-catalog.ts", import.meta.url) +const README_PATH = new URL("../../README.md", import.meta.url) +const CHANGELOG_PATH = new URL("../../CHANGELOG.md", import.meta.url) export interface CommandCodeModelMetadata { imageModelIds: readonly string[] @@ -20,6 +26,7 @@ export interface CommandCodeModelMetadata { } export interface ModelMetadataDiff { + versionChanged: boolean addedImageModelIds: readonly string[] removedImageModelIds: readonly string[] addedReasoningModelIds: readonly string[] @@ -144,6 +151,8 @@ export function currentModelMetadata(): CommandCodeModelMetadata { export function diffModelMetadata( current: CommandCodeModelMetadata, upstream: CommandCodeModelMetadata, + currentVersion = COMMAND_CODE_CLI_VERSION, + upstreamVersion = COMMAND_CODE_CLI_VERSION, ): ModelMetadataDiff { const currentImages = new Set(current.imageModelIds) const upstreamImages = new Set(upstream.imageModelIds) @@ -153,6 +162,7 @@ export function diffModelMetadata( const upstreamReasoningSet = new Set(upstreamReasoningIds) return { + versionChanged: currentVersion !== upstreamVersion, addedImageModelIds: sorted( upstream.imageModelIds.filter((modelId) => !currentImages.has(modelId)), ), @@ -177,7 +187,10 @@ export function diffModelMetadata( } export function hasModelMetadataDiff(diff: ModelMetadataDiff): boolean { - return Object.values(diff).some((modelIds) => modelIds.length > 0) + return ( + diff.versionChanged || + Object.entries(diff).some(([key, modelIds]) => key !== "versionChanged" && modelIds.length > 0) + ) } function formatList(modelIds: readonly string[]): string { @@ -200,6 +213,66 @@ function formatReasoningChanges( .join("
") } +function quoted(value: string): string { + return JSON.stringify(value) +} + +function recordEntries( + values: Readonly>, +): readonly [string, readonly string[]][] { + return Object.entries(values).sort(([left], [right]) => left.localeCompare(right)) +} + +export function renderCommandCodeCatalog( + packageVersion: string, + metadata: CommandCodeModelMetadata, +): string { + const imageEntries = sorted(metadata.imageModelIds) + .map((modelId) => ` ${quoted(modelId)}: ["text", "image"],`) + .join("\n") + const reasoningEntries = recordEntries(metadata.reasoningEfforts) + .map( + ([modelId, efforts]) => + ` ${quoted(modelId)}: [${efforts.map((effort) => quoted(effort)).join(", ")}],`, + ) + .join("\n") + + return `export const COMMAND_CODE_CLI_VERSION = ${quoted(packageVersion)}\n\nexport type CommandCodeInputType = "text" | "image"\nexport type CommandCodeReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max"\n\n/**\n * Generated from command-code@${packageVersion} by \`npm run sync:commandcode-catalog\`.\n * Do not edit manually.\n */\nexport const MODEL_INPUT_MODALITIES: Readonly> = {\n${imageEntries}\n}\n\nexport const MODEL_EFFORTS: Readonly> = {\n${reasoningEntries}\n}\n` +} + +function updateDocumentedCatalogVersion( + contents: string, + packageVersion: string, + context: string, +): string { + const pattern = /command-code@\d+\.\d+\.\d+(?:[-+][^`\s,]+)?/ + if (!pattern.test(contents)) throw new Error(`Could not find the ${context} catalog version`) + return contents.replace(pattern, `command-code@${packageVersion}`) +} + +export function updateReadmeCatalogVersion(readme: string, packageVersion: string): string { + return updateDocumentedCatalogVersion(readme, packageVersion, "README") +} + +export function updateChangelogCatalogVersion(changelog: string, packageVersion: string): string { + return updateDocumentedCatalogVersion(changelog, packageVersion, "changelog") +} + +async function writeSynchronizedCatalog( + packageVersion: string, + metadata: CommandCodeModelMetadata, +): Promise { + const [readme, changelog] = await Promise.all([ + readFile(README_PATH, "utf-8"), + readFile(CHANGELOG_PATH, "utf-8"), + ]) + await Promise.all([ + writeFile(CATALOG_SOURCE_PATH, renderCommandCodeCatalog(packageVersion, metadata), "utf-8"), + writeFile(README_PATH, updateReadmeCatalogVersion(readme, packageVersion), "utf-8"), + writeFile(CHANGELOG_PATH, updateChangelogCatalogVersion(changelog, packageVersion), "utf-8"), + ]) +} + function metadataReport( packageVersion: string, current: CommandCodeModelMetadata, @@ -219,6 +292,7 @@ function metadataReport( "", "| Change | Models |", "| --- | --- |", + `| CLI version | ${diff.versionChanged ? `\`${COMMAND_CODE_CLI_VERSION}\` → \`${packageVersion}\`` : "Current"} |`, `| New image support | ${formatList(diff.addedImageModelIds)} |`, `| Removed image support | ${formatList(diff.removedImageModelIds)} |`, `| New reasoning metadata | ${formatList(diff.addedReasoningModelIds)} |`, @@ -289,10 +363,17 @@ async function inspectPackedPackage(packageSpec: string): Promise<{ } async function main(): Promise { - const packageSpec = process.argv[2] ?? "command-code@latest" + const write = process.argv.includes("--write") + const packageSpec = + process.argv.find((argument) => argument.startsWith("command-code@")) ?? "command-code@latest" const current = currentModelMetadata() const upstreamPackage = await inspectPackedPackage(packageSpec) - const diff = diffModelMetadata(current, upstreamPackage.metadata) + const diff = diffModelMetadata( + current, + upstreamPackage.metadata, + COMMAND_CODE_CLI_VERSION, + upstreamPackage.packageVersion, + ) const report = metadataReport( upstreamPackage.packageVersion, current, @@ -302,6 +383,12 @@ async function main(): Promise { console.log(report) + if (write) { + await writeSynchronizedCatalog(upstreamPackage.packageVersion, upstreamPackage.metadata) + console.log(`Synchronized static metadata with command-code@${upstreamPackage.packageVersion}.`) + return + } + if (hasModelMetadataDiff(diff)) { throw new Error( `Static model metadata differs from command-code@${upstreamPackage.packageVersion}. Update src/models.ts and the snapshot version.`, diff --git a/package.json b/package.json index 8d14353..660b6c1 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "format": "prettier --write '**/*.{ts,mjs,json,md}'", "pi:isolated": "node scripts/pi-isolated.mjs", "pi:authenticated": "node scripts/pi-authenticated.mjs", - "check:model-metadata": "tsx .github/scripts/check-commandcode-model-metadata.ts command-code@latest", + "check:commandcode-catalog": "tsx .github/scripts/check-commandcode-model-metadata.ts command-code@latest", + "sync:commandcode-catalog": "tsx .github/scripts/check-commandcode-model-metadata.ts command-code@latest --write", "test:quota": "tsx tests/test-quota.ts && tsx tests/test-quota-command.ts", "test:unit": "tsx tests/test-api-key.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-model-metadata-check.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-quota.ts && tsx tests/test-quota-command.ts && tsx tests/test-retry.ts && tsx tests/test-transport.ts", "test:api-key": "tsx tests/test-api-key.ts", diff --git a/src/commandcode-catalog.ts b/src/commandcode-catalog.ts new file mode 100644 index 0000000..4249514 --- /dev/null +++ b/src/commandcode-catalog.ts @@ -0,0 +1,84 @@ +export const COMMAND_CODE_CLI_VERSION = "1.32.2" + +export type CommandCodeInputType = "text" | "image" +export type CommandCodeReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max" + +/** + * Generated from command-code@1.32.2 by `npm run sync:commandcode-catalog`. + * Do not edit manually. + */ +export const MODEL_INPUT_MODALITIES: Readonly> = { + "claude-fable-5": ["text", "image"], + "claude-haiku-4-5-20251001": ["text", "image"], + "claude-opus-4-7": ["text", "image"], + "claude-opus-4-8": ["text", "image"], + "claude-opus-5": ["text", "image"], + "claude-sonnet-4-6": ["text", "image"], + "claude-sonnet-5": ["text", "image"], + "deepseek/deepseek-v4-flash-vision-exp": ["text", "image"], + "google/gemini-3.1-flash-lite": ["text", "image"], + "google/gemini-3.5-flash": ["text", "image"], + "google/gemini-3.5-flash-lite": ["text", "image"], + "google/gemini-3.6-flash": ["text", "image"], + "google/gemini-3.7-flash": ["text", "image"], + "gpt-5.3-codex": ["text", "image"], + "gpt-5.4": ["text", "image"], + "gpt-5.4-mini": ["text", "image"], + "gpt-5.5": ["text", "image"], + "gpt-5.6-luna": ["text", "image"], + "gpt-5.6-sol": ["text", "image"], + "gpt-5.6-terra": ["text", "image"], + "meta/muse-spark-1.1": ["text", "image"], + "meta/muse-spark-1.2": ["text", "image"], + "meta/muse-spark-1.2-contributor": ["text", "image"], + "MiniMaxAI/MiniMax-M3": ["text", "image"], + "moonshotai/Kimi-K2.5": ["text", "image"], + "moonshotai/Kimi-K2.6": ["text", "image"], + "moonshotai/Kimi-K2.7-Code": ["text", "image"], + "moonshotai/Kimi-K2.7-Code-Highspeed": ["text", "image"], + "moonshotai/Kimi-K3": ["text", "image"], + "Qwen/Qwen3.6-Plus": ["text", "image"], + "Qwen/Qwen3.7-Flash": ["text", "image"], + "Qwen/Qwen3.7-Plus": ["text", "image"], + "Qwen/Qwen3.8-27B": ["text", "image"], + "Qwen/Qwen3.8-Max": ["text", "image"], + "sakana/fugu-ultra": ["text", "image"], + "stealth/ox-alpha": ["text", "image"], + "stepfun/Step-3.7-Flash": ["text", "image"], + "thinkingmachines/inkling": ["text", "image"], + "thinkingmachines/inkling-small": ["text", "image"], + "xai/grok-4.5": ["text", "image"], + "xiaomi/mimo-v2.5": ["text", "image"], +} + +export const MODEL_EFFORTS: Readonly> = { + "claude-fable-5": ["low", "medium", "high", "xhigh", "max"], + "claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"], + "claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"], + "claude-opus-5": ["low", "medium", "high", "xhigh", "max"], + "claude-sonnet-4-6": ["low", "medium", "high", "xhigh", "max"], + "claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"], + "deepseek/deepseek-v4-flash": ["high", "max"], + "deepseek/deepseek-v4-flash-vision-exp": ["high", "max"], + "deepseek/deepseek-v4-pro": ["high", "max"], + "google/gemini-3.1-flash-lite": ["low", "medium", "high"], + "google/gemini-3.5-flash": ["low", "medium", "high"], + "google/gemini-3.5-flash-lite": ["low", "medium", "high"], + "google/gemini-3.6-flash": ["low", "medium", "high"], + "google/gemini-3.7-flash": ["low", "medium", "high"], + "gpt-5.3-codex": ["low", "medium", "high", "xhigh"], + "gpt-5.4": ["low", "medium", "high", "xhigh"], + "gpt-5.4-mini": ["low", "medium", "high"], + "gpt-5.5": ["low", "medium", "high", "xhigh"], + "gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"], + "gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max"], + "gpt-5.6-terra": ["low", "medium", "high", "xhigh", "max"], + "Qwen/Qwen3.8-27B": ["low", "medium", "xhigh"], + "Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"], + "sakana/fugu-ultra": ["high", "xhigh"], + "stealth/ox-alpha": ["low", "high", "max"], + "xai/grok-4.5": ["low", "medium", "high"], + "xai/grok-4.6": ["low", "medium", "high", "xhigh"], + "zai-org/GLM-5.2": ["high", "max"], + "zai-org/GLM-5.3": ["low", "high", "max"], +} diff --git a/src/core.ts b/src/core.ts index d2b95f8..57ed455 100644 --- a/src/core.ts +++ b/src/core.ts @@ -7,6 +7,7 @@ import { randomUUID } from "node:crypto" +import { COMMAND_CODE_CLI_VERSION } from "./commandcode-catalog.ts" import { commandCodeErrorMessage, redactCommandCodeErrorText } from "./overflow.ts" import { modelSupportsImageInput } from "./models.ts" import { @@ -43,7 +44,7 @@ export * from "./overflow.ts" export * from "./types.ts" export const DEFAULT_API_BASE = "https://api.commandcode.ai" -export const COMMAND_CODE_CLI_VERSION = "1.32.2" +export { COMMAND_CODE_CLI_VERSION } const DEFAULT_GENERATE_MAX_TOKENS = 64_000 const DEFAULT_MAX_RETRIES = 0 diff --git a/src/models.ts b/src/models.ts index 0b30631..9b1ec93 100644 --- a/src/models.ts +++ b/src/models.ts @@ -1,6 +1,16 @@ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises" import { dirname } from "node:path" +import { + MODEL_EFFORTS, + MODEL_INPUT_MODALITIES, + type CommandCodeInputType, + type CommandCodeReasoningEffort, +} from "./commandcode-catalog.ts" + +export { MODEL_EFFORTS, MODEL_INPUT_MODALITIES } +export type { CommandCodeInputType } + export const DEFAULT_PROVIDER_API_BASE = "https://api.commandcode.ai/provider/v1" export const DEFAULT_MODELS_URL = `${DEFAULT_PROVIDER_API_BASE}/models` export const DEFAULT_MODELS_TIMEOUT_MS = 10_000 @@ -9,56 +19,6 @@ const DEFAULT_MAX_OUTPUT_TOKENS = 65_536 const MODEL_CACHE_VERSION = 1 export type CommandCodeApi = "openai-completions" | "anthropic-messages" -export type CommandCodeInputType = "text" | "image" - -/** - * Model input modalities from the command-code@1.32.2 bundled catalog. - * Models omitted here remain text-only so newly discovered IDs never claim - * image support without upstream evidence. - */ -export const MODEL_INPUT_MODALITIES: Readonly> = { - "MiniMaxAI/MiniMax-M3": ["text", "image"], - "Qwen/Qwen3.6-Plus": ["text", "image"], - "Qwen/Qwen3.7-Flash": ["text", "image"], - "Qwen/Qwen3.7-Plus": ["text", "image"], - "Qwen/Qwen3.8-27B": ["text", "image"], - "Qwen/Qwen3.8-Max": ["text", "image"], - "claude-fable-5": ["text", "image"], - "claude-haiku-4-5-20251001": ["text", "image"], - "claude-opus-4-7": ["text", "image"], - "claude-opus-4-8": ["text", "image"], - "claude-opus-5": ["text", "image"], - "claude-sonnet-4-6": ["text", "image"], - "claude-sonnet-5": ["text", "image"], - "deepseek/deepseek-v4-flash-vision-exp": ["text", "image"], - "google/gemini-3.1-flash-lite": ["text", "image"], - "google/gemini-3.5-flash": ["text", "image"], - "google/gemini-3.5-flash-lite": ["text", "image"], - "google/gemini-3.6-flash": ["text", "image"], - "google/gemini-3.7-flash": ["text", "image"], - "gpt-5.3-codex": ["text", "image"], - "gpt-5.4": ["text", "image"], - "gpt-5.4-mini": ["text", "image"], - "gpt-5.5": ["text", "image"], - "gpt-5.6-luna": ["text", "image"], - "gpt-5.6-sol": ["text", "image"], - "gpt-5.6-terra": ["text", "image"], - "meta/muse-spark-1.1": ["text", "image"], - "meta/muse-spark-1.2": ["text", "image"], - "meta/muse-spark-1.2-contributor": ["text", "image"], - "moonshotai/Kimi-K2.5": ["text", "image"], - "moonshotai/Kimi-K2.6": ["text", "image"], - "moonshotai/Kimi-K2.7-Code": ["text", "image"], - "moonshotai/Kimi-K2.7-Code-Highspeed": ["text", "image"], - "moonshotai/Kimi-K3": ["text", "image"], - "sakana/fugu-ultra": ["text", "image"], - "stealth/ox-alpha": ["text", "image"], - "stepfun/Step-3.7-Flash": ["text", "image"], - "thinkingmachines/inkling": ["text", "image"], - "thinkingmachines/inkling-small": ["text", "image"], - "xai/grok-4.5": ["text", "image"], - "xiaomi/mimo-v2.5": ["text", "image"], -} const TEXT_INPUT_ONLY = ["text"] as const @@ -72,49 +32,6 @@ export function modelSupportsImageInput(modelId: string): boolean { export type PiThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" -type CommandCodeReasoningEffort = Exclude - -/** - * Per-model reasoning efforts supported by Command Code's generate endpoint. - * - * The Provider API does not expose reasoning metadata. This is an exact - * snapshot of `reasoningEfforts` from the command-code@1.32.2 model catalog - * (`packages/shared/src/model-catalog.ts`, also published in the generated - * `dist/bundled/command-code-knowledge/reference/models.md`). Models omitted - * here let Command Code choose their reasoning depth, matching the CLI. - */ -export const MODEL_EFFORTS: Readonly> = { - "Qwen/Qwen3.8-27B": ["low", "medium", "xhigh"], - "Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"], - "claude-fable-5": ["low", "medium", "high", "xhigh", "max"], - "claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"], - "claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"], - "claude-opus-5": ["low", "medium", "high", "xhigh", "max"], - "claude-sonnet-4-6": ["low", "medium", "high", "xhigh", "max"], - "claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"], - "deepseek/deepseek-v4-flash": ["high", "max"], - "deepseek/deepseek-v4-flash-vision-exp": ["high", "max"], - "deepseek/deepseek-v4-pro": ["high", "max"], - "gpt-5.3-codex": ["low", "medium", "high", "xhigh"], - "gpt-5.4": ["low", "medium", "high", "xhigh"], - "gpt-5.4-mini": ["low", "medium", "high"], - "gpt-5.5": ["low", "medium", "high", "xhigh"], - "gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"], - "gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max"], - "gpt-5.6-terra": ["low", "medium", "high", "xhigh", "max"], - "google/gemini-3.1-flash-lite": ["low", "medium", "high"], - "google/gemini-3.5-flash": ["low", "medium", "high"], - "google/gemini-3.5-flash-lite": ["low", "medium", "high"], - "google/gemini-3.6-flash": ["low", "medium", "high"], - "google/gemini-3.7-flash": ["low", "medium", "high"], - "sakana/fugu-ultra": ["high", "xhigh"], - "stealth/ox-alpha": ["low", "high", "max"], - "xai/grok-4.5": ["low", "medium", "high"], - "xai/grok-4.6": ["low", "medium", "high", "xhigh"], - "zai-org/GLM-5.2": ["high", "max"], - "zai-org/GLM-5.3": ["low", "high", "max"], -} - const PI_THINKING_LEVELS: readonly PiThinkingLevel[] = [ "off", "minimal", diff --git a/tests/test-model-metadata-check.ts b/tests/test-model-metadata-check.ts index 145b298..c2989b6 100644 --- a/tests/test-model-metadata-check.ts +++ b/tests/test-model-metadata-check.ts @@ -8,6 +8,9 @@ import { parseKnownTextOnlyModelIds, parseModelsReference, parsePackageVersion, + renderCommandCodeCatalog, + updateChangelogCatalogVersion, + updateReadmeCatalogVersion, type CommandCodeModelMetadata, } from "../.github/scripts/check-commandcode-model-metadata.ts" @@ -68,6 +71,7 @@ describe("Command Code model metadata checker", () => { const diff = diffModelMetadata(current, upstream) assert.deepEqual(diff, { + versionChanged: false, addedImageModelIds: ["added-image"], removedImageModelIds: ["removed-image"], addedReasoningModelIds: ["added-reasoning"], @@ -77,6 +81,63 @@ describe("Command Code model metadata checker", () => { assert.equal(hasModelMetadataDiff(diff), true) }) + it("reports CLI version drift even when model metadata is unchanged", () => { + const metadata: CommandCodeModelMetadata = { + imageModelIds: ["vision-model"], + reasoningEfforts: { "vision-model": ["low"] }, + } + + const diff = diffModelMetadata(metadata, metadata, "1.32.2", "1.33.0") + + assert.equal(diff.versionChanged, true) + assert.equal(hasModelMetadataDiff(diff), true) + }) + + it("renders a deterministic generated catalog and updates the README version", () => { + assert.equal( + renderCommandCodeCatalog("1.33.0", { + imageModelIds: ["b-model", "a-model"], + reasoningEfforts: { + "b-model": ["high", "max"], + "a-model": ["low"], + }, + }), + `export const COMMAND_CODE_CLI_VERSION = "1.33.0" + +export type CommandCodeInputType = "text" | "image" +export type CommandCodeReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max" + +/** + * Generated from command-code@1.33.0 by \`npm run sync:commandcode-catalog\`. + * Do not edit manually. + */ +export const MODEL_INPUT_MODALITIES: Readonly> = { + "a-model": ["text", "image"], + "b-model": ["text", "image"], +} + +export const MODEL_EFFORTS: Readonly> = { + "a-model": ["low"], + "b-model": ["high", "max"], +} +`, + ) + assert.equal( + updateReadmeCatalogVersion( + "The capability snapshot currently follows `command-code@1.32.2`.", + "1.33.0", + ), + "The capability snapshot currently follows `command-code@1.33.0`.", + ) + assert.equal( + updateChangelogCatalogVersion( + "- Refresh capabilities from `command-code@1.32.2`, including metadata.", + "1.33.0", + ), + "- Refresh capabilities from `command-code@1.33.0`, including metadata.", + ) + }) + it("rejects unexpected upstream structures instead of silently passing", () => { assert.throws(() => parseModelsReference("# no catalog"), /No model rows/) assert.throws( diff --git a/tests/test-models.ts b/tests/test-models.ts index f283400..391e06b 100644 --- a/tests/test-models.ts +++ b/tests/test-models.ts @@ -4,6 +4,7 @@ import { tmpdir } from "node:os" import { join } from "node:path" import { describe, it } from "node:test" +import { COMMAND_CODE_CLI_VERSION } from "../src/commandcode-catalog.ts" import { apiForModelId, baseUrlForModel, @@ -100,7 +101,7 @@ describe("commandCodeModelsFromApiResponse()", () => { ) }) - it("matches command-code@1.32.2 image input capabilities", () => { + it(`uses the command-code@${COMMAND_CODE_CLI_VERSION} image capability catalog`, () => { assert.deepEqual(inputModalitiesForModel("gpt-5.6-luna"), ["text", "image"]) assert.deepEqual(inputModalitiesForModel("meta/muse-spark-1.2"), ["text", "image"]) assert.deepEqual(inputModalitiesForModel("deepseek/deepseek-v4-flash-vision-exp"), [ @@ -117,7 +118,10 @@ describe("commandCodeModelsFromApiResponse()", () => { assert.equal(modelSupportsImageInput("deepseek/deepseek-v4-flash-vision-exp"), true) assert.equal(modelSupportsImageInput("stealth/ox-alpha"), true) assert.equal(modelSupportsImageInput("deepseek/deepseek-v4-pro"), false) - assert.equal(Object.keys(MODEL_INPUT_MODALITIES).length, 41) + assert.ok(Object.keys(MODEL_INPUT_MODALITIES).length > 0) + for (const modalities of Object.values(MODEL_INPUT_MODALITIES)) { + assert.deepEqual(modalities, ["text", "image"]) + } }) it("marks only known reasoning models as reasoning-capable", () => { @@ -133,38 +137,14 @@ describe("commandCodeModelsFromApiResponse()", () => { assert.equal(models[1]?.reasoning, false) }) - it("matches the exact command-code@1.32.2 reasoning effort catalog", () => { - assert.deepEqual(MODEL_EFFORTS, { - "Qwen/Qwen3.8-27B": ["low", "medium", "xhigh"], - "Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"], - "claude-fable-5": ["low", "medium", "high", "xhigh", "max"], - "claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"], - "claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"], - "claude-opus-5": ["low", "medium", "high", "xhigh", "max"], - "claude-sonnet-4-6": ["low", "medium", "high", "xhigh", "max"], - "claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"], - "deepseek/deepseek-v4-flash": ["high", "max"], - "deepseek/deepseek-v4-flash-vision-exp": ["high", "max"], - "deepseek/deepseek-v4-pro": ["high", "max"], - "gpt-5.3-codex": ["low", "medium", "high", "xhigh"], - "gpt-5.4": ["low", "medium", "high", "xhigh"], - "gpt-5.4-mini": ["low", "medium", "high"], - "gpt-5.5": ["low", "medium", "high", "xhigh"], - "gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"], - "gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max"], - "gpt-5.6-terra": ["low", "medium", "high", "xhigh", "max"], - "google/gemini-3.1-flash-lite": ["low", "medium", "high"], - "google/gemini-3.5-flash": ["low", "medium", "high"], - "google/gemini-3.5-flash-lite": ["low", "medium", "high"], - "google/gemini-3.6-flash": ["low", "medium", "high"], - "google/gemini-3.7-flash": ["low", "medium", "high"], - "sakana/fugu-ultra": ["high", "xhigh"], - "stealth/ox-alpha": ["low", "high", "max"], - "xai/grok-4.5": ["low", "medium", "high"], - "xai/grok-4.6": ["low", "medium", "high", "xhigh"], - "zai-org/GLM-5.2": ["high", "max"], - "zai-org/GLM-5.3": ["low", "high", "max"], - }) + it(`uses the command-code@${COMMAND_CODE_CLI_VERSION} reasoning effort catalog`, () => { + const validEfforts = new Set(["minimal", "low", "medium", "high", "xhigh", "max"]) + assert.ok(Object.keys(MODEL_EFFORTS).length > 0) + for (const efforts of Object.values(MODEL_EFFORTS)) { + assert.ok(efforts.length > 0) + assert.equal(new Set(efforts).size, efforts.length) + assert.ok(efforts.every((effort) => validEfforts.has(effort))) + } }) it("builds separate canonical pi and OMP metadata", () => { diff --git a/tests/test-stream.ts b/tests/test-stream.ts index 6346416..a015e75 100644 --- a/tests/test-stream.ts +++ b/tests/test-stream.ts @@ -6,6 +6,7 @@ import assert from "node:assert/strict" import { after, before, beforeEach, describe, it } from "node:test" +import { COMMAND_CODE_CLI_VERSION } from "../src/commandcode-catalog.ts" import type { AssistantMessageEvent } from "../src/core.ts" import { MODEL_EFFORTS, thinkingLevelMapForEfforts } from "../src/models.ts" import { @@ -569,7 +570,7 @@ describe("streamCommandCode — request serialization", () => { const headers = server.lastRequestHeaders() assert.equal(headers.authorization, "Bearer mock-key") - assert.equal(headers["x-command-code-version"], "1.32.2") + assert.equal(headers["x-command-code-version"], COMMAND_CODE_CLI_VERSION) assert.equal(headers["x-project-slug"], "repo") assert.equal(headers["x-taste-learning"], "true") assert.equal(headers["x-co-flag"], "false") From c3a383447cbe96f04d10c2483e1566d7ba318485 Mon Sep 17 00:00:00 2001 From: Patrick Wozniak Date: Tue, 25 Aug 2026 15:08:29 +0200 Subject: [PATCH 7/8] ci(models): open PRs for Command Code catalog updates --- .github/workflows/model-metadata.yml | 69 ++++++++++++++++++++++++---- CHANGELOG.md | 2 +- README.md | 2 +- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/.github/workflows/model-metadata.yml b/.github/workflows/model-metadata.yml index 809eef8..196b7b5 100644 --- a/.github/workflows/model-metadata.yml +++ b/.github/workflows/model-metadata.yml @@ -1,4 +1,4 @@ -name: Command Code model metadata +name: Command Code catalog sync on: pull_request: @@ -6,6 +6,7 @@ on: paths: - ".github/scripts/check-commandcode-model-metadata.ts" - ".github/workflows/model-metadata.yml" + - "src/commandcode-catalog.ts" - "src/core.ts" - "src/models.ts" - "tests/test-model-metadata-check.ts" @@ -13,17 +14,17 @@ on: - cron: "17 6 * * *" workflow_dispatch: -permissions: - contents: read - concurrency: - group: commandcode-model-metadata + group: commandcode-catalog-${{ github.event_name == 'pull_request' && github.event.pull_request.number || 'sync' }} cancel-in-progress: true jobs: check: + if: github.event_name == 'pull_request' runs-on: ubuntu-latest timeout-minutes: 10 + permissions: + contents: read steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -32,8 +33,58 @@ jobs: cache: npm registry-url: https://registry.npmjs.org - run: npm ci - - name: Compare static metadata with the latest Command Code CLI - run: npm run check:model-metadata | tee model-metadata-report.md - - name: Publish metadata report + - name: Compare with the latest Command Code CLI + run: npm run check:commandcode-catalog | tee commandcode-catalog-report.md + - name: Publish catalog report if: always() - run: cat model-metadata-report.md >> "$GITHUB_STEP_SUMMARY" + run: cat commandcode-catalog-report.md >> "$GITHUB_STEP_SUMMARY" + + sync: + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + registry-url: https://registry.npmjs.org + - run: npm ci + - name: Synchronize with the latest Command Code CLI + run: npm run sync:commandcode-catalog | tee commandcode-catalog-report.md + - name: Format and verify synchronized files + run: | + npm run format -- src/commandcode-catalog.ts README.md CHANGELOG.md + npm run typecheck + npm run test:models + npm run format:check + git diff --check + - name: Publish catalog report + if: always() + run: cat commandcode-catalog-report.md >> "$GITHUB_STEP_SUMMARY" + - name: Create or update synchronization PR + uses: peter-evans/create-pull-request@v8 + with: + branch: automation/commandcode-catalog + delete-branch: true + commit-message: "chore(models): sync Command Code catalog" + title: "chore(models): sync Command Code catalog" + body: | + Automated synchronization with the latest published `command-code` CLI package. + + This updates only machine-readable compatibility metadata: + - CLI version used in the `x-command-code-version` header + - image-input capabilities + - supported reasoning efforts + - documented catalog snapshot version + + Pricing remains review-only because CLI documentation does not represent every pricing tier and temporary promotion used by the provider. + assignees: patlux + add-paths: | + src/commandcode-catalog.ts + README.md + CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c4e2930..5730962 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -- Add a daily GitHub Actions check that compares static image and reasoning metadata with the latest published Command Code CLI catalog. +- Add a daily GitHub Actions synchronization job that opens or updates a pull request for CLI version, image capability, and reasoning-effort changes in the latest published Command Code catalog. - Refresh static model capabilities from `command-code@1.32.2`, including new image and reasoning metadata. - Add `/commandcode-quota` with live credits, plan, usage totals, and rolling-limit diagnostics from Command Code's alpha usage endpoints. - Add `zai-org/GLM-5.3` with its verified reasoning efforts and display pricing. diff --git a/README.md b/README.md index 7657981..83c8295 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ The following environment variables are intended for tests, local mocks, and com ## Image input -The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.2`; unknown models default to text-only until their upstream metadata is reviewed. A daily GitHub Actions check compares the static image and reasoning metadata with the latest published CLI package and reports any drift. +The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.2`; unknown models default to text-only until their upstream metadata is reviewed. A daily GitHub Actions job synchronizes the CLI version, image capabilities, and reasoning efforts with the latest published CLI package and opens or updates a reviewable pull request when they change. Pricing remains manually reviewed because the CLI catalog does not expose every pricing tier and temporary promotion used by the provider. For vision-capable models, Pi's native provider adapters forward image blocks from user messages and tool results using the documented OpenAI or Anthropic message schema. Unknown and text-only models remain marked text-only in Pi. From 41e98a1d67a7495ecb3126e4c73fbfebea4343ba Mon Sep 17 00:00:00 2001 From: Patrick Wozniak Date: Tue, 25 Aug 2026 15:09:54 +0200 Subject: [PATCH 8/8] fix(models): keep generated sync scope reviewable --- .github/scripts/check-commandcode-model-metadata.ts | 11 +---------- .github/workflows/model-metadata.yml | 3 +-- tests/test-model-metadata-check.ts | 8 -------- 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/scripts/check-commandcode-model-metadata.ts b/.github/scripts/check-commandcode-model-metadata.ts index 4cfa4e2..292a4d3 100644 --- a/.github/scripts/check-commandcode-model-metadata.ts +++ b/.github/scripts/check-commandcode-model-metadata.ts @@ -18,7 +18,6 @@ const TEXT_ONLY_MARKER = ',__name(isKnownTextOnlyModel,"isKnownTextOnlyModel")' const VALID_EFFORTS = new Set(["minimal", "low", "medium", "high", "xhigh", "max"]) const CATALOG_SOURCE_PATH = new URL("../../src/commandcode-catalog.ts", import.meta.url) const README_PATH = new URL("../../README.md", import.meta.url) -const CHANGELOG_PATH = new URL("../../CHANGELOG.md", import.meta.url) export interface CommandCodeModelMetadata { imageModelIds: readonly string[] @@ -254,22 +253,14 @@ export function updateReadmeCatalogVersion(readme: string, packageVersion: strin return updateDocumentedCatalogVersion(readme, packageVersion, "README") } -export function updateChangelogCatalogVersion(changelog: string, packageVersion: string): string { - return updateDocumentedCatalogVersion(changelog, packageVersion, "changelog") -} - async function writeSynchronizedCatalog( packageVersion: string, metadata: CommandCodeModelMetadata, ): Promise { - const [readme, changelog] = await Promise.all([ - readFile(README_PATH, "utf-8"), - readFile(CHANGELOG_PATH, "utf-8"), - ]) + const readme = await readFile(README_PATH, "utf-8") await Promise.all([ writeFile(CATALOG_SOURCE_PATH, renderCommandCodeCatalog(packageVersion, metadata), "utf-8"), writeFile(README_PATH, updateReadmeCatalogVersion(readme, packageVersion), "utf-8"), - writeFile(CHANGELOG_PATH, updateChangelogCatalogVersion(changelog, packageVersion), "utf-8"), ]) } diff --git a/.github/workflows/model-metadata.yml b/.github/workflows/model-metadata.yml index 196b7b5..de5a26f 100644 --- a/.github/workflows/model-metadata.yml +++ b/.github/workflows/model-metadata.yml @@ -58,7 +58,7 @@ jobs: run: npm run sync:commandcode-catalog | tee commandcode-catalog-report.md - name: Format and verify synchronized files run: | - npm run format -- src/commandcode-catalog.ts README.md CHANGELOG.md + npm run format -- src/commandcode-catalog.ts README.md npm run typecheck npm run test:models npm run format:check @@ -87,4 +87,3 @@ jobs: add-paths: | src/commandcode-catalog.ts README.md - CHANGELOG.md diff --git a/tests/test-model-metadata-check.ts b/tests/test-model-metadata-check.ts index c2989b6..ac1b61f 100644 --- a/tests/test-model-metadata-check.ts +++ b/tests/test-model-metadata-check.ts @@ -9,7 +9,6 @@ import { parseModelsReference, parsePackageVersion, renderCommandCodeCatalog, - updateChangelogCatalogVersion, updateReadmeCatalogVersion, type CommandCodeModelMetadata, } from "../.github/scripts/check-commandcode-model-metadata.ts" @@ -129,13 +128,6 @@ export const MODEL_EFFORTS: Readonly {