Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .agents/skills/testing-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ suites are outside this distribution's verification.
| Permission facade on macOS/Linux | `pnpm test:policy` |
| Sandbox on macOS | `pnpm test:sandbox` |
| Source-sync, workflow and release tools | `pnpm test:release-tools` |
| npm release archive installation | `MCODE_RELEASE_TAG=vX.Y.Z MCODE_RELEASE_ARCHIVE=/path/to/package.tar.gz pnpm verify --profile package` |
| Types and standalone build boundary | `pnpm typecheck`, `pnpm build`, `pnpm check:standalone` |
| Published files and generated paths | `pnpm check:source`, `pnpm check:tsconfig` |

Expand Down Expand Up @@ -61,6 +62,13 @@ Skills under `.agents/skills`, unknown paths and inventory changes require the
full profile. The `archive` profile is for source-archive validation, not a way to
bypass the clean-commit export requirement.

The `package` profile authenticates an npm release archive, installs it into a
temporary npm prefix, and exercises its launcher, native dependencies and offline
smoke/BYOK suites. It requires `MCODE_RELEASE_TAG` and `MCODE_RELEASE_ARCHIVE` and
does not replace source validation. The release command commits matching root/TUI source versions before tagging.
The release workflow rejects version mismatches and runs the full profile before
package installation checks.

## Manual evidence and reporting

For CLI behavior, exercise the built `dist/cli.js` and inspect stdout, stderr and
Expand Down
146 changes: 146 additions & 0 deletions .github/workflows/cli-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
name: CLI release
on:
push:
tags: ['v*']
pull_request:
paths:
- '.github/workflows/cli-release.yml'
- 'scripts/build.mjs'
- 'scripts/*cli-release.mjs'
- 'scripts/release-cli.mjs'
- 'scripts/lib/cli-release.mjs'
- 'scripts/verify.mjs'
- 'test/smoke.test.mjs'
- 'test/byok.test.mjs'
- 'pnpm-lock.yaml'
- 'package.json'
- 'packages/tui/package.json'
workflow_dispatch:
inputs:
tag:
description: 'Optional tag matching the source version (dry run; does not publish)'
required: false
type: string
permissions:
contents: read
concurrency:
group: cli-release-${{ inputs.tag || github.ref }}
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 25
outputs:
version: ${{ steps.release.outputs.version }}
matrix: ${{ steps.release.outputs.matrix }}
tag: ${{ steps.release.outputs.tag }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
persist-credentials: false
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
cache: pnpm
- name: Validate release version and revision
id: release
shell: bash
run: |
node --input-type=module -e 'import { appendFileSync } from "node:fs"; import { cliBuildVersion, cliReleaseTargets } from "./scripts/lib/cli-release.mjs"; const tag = process.env.REQUESTED_TAG || `v${cliBuildVersion(process.cwd())}`; const version = cliBuildVersion(process.cwd(), tag); appendFileSync(process.env.GITHUB_OUTPUT, `version=${version}\ntag=${tag}\nmatrix=${JSON.stringify({include: cliReleaseTargets})}\n`); appendFileSync(process.env.GITHUB_ENV, `MCODE_RELEASE_TAG=${tag}\n`);'
if [ "$EVENT_NAME" = push ]; then
test "$(git rev-parse "refs/tags/$REQUESTED_TAG^{commit}")" = "$(git rev-parse HEAD)"
fi
env:
EVENT_NAME: ${{ github.event_name }}
REQUESTED_TAG: ${{ inputs.tag || (github.event_name == 'push' && github.ref_name) || '' }}
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/setup-gitleaks
- name: Scan source history
run: gitleaks git --redact --config .gitleaks.toml --log-opts=--all
- run: pnpm verify
env:
MCODE_VERIFY_REPORT_DIR: ${{ runner.temp }}/build-report
- name: Scan distribution
run: gitleaks dir dist --redact --config .gitleaks.toml
- name: Package the tagged CLI
shell: bash
run: node scripts/package-cli-release.mjs "$MCODE_RELEASE_TAG" "$RUNNER_TEMP/package"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: cli-package
path: ${{ runner.temp }}/package/
retention-days: 14
if-no-files-found: error
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
if: ${{ always() }}
with:
name: cli-build-report
path: ${{ runner.temp }}/build-report/
retention-days: 14
install:
needs: build
env:
MCODE_RELEASE_TAG: ${{ needs.build.outputs.tag }}
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.build.outputs.matrix) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ matrix.node }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: cli-package
path: ${{ runner.temp }}/package
- run: pnpm verify --profile package
env:
MCODE_RELEASE_ARCHIVE: ${{ runner.temp }}/package/minimax-code-${{ needs.build.outputs.version }}.tar.gz
MCODE_VERIFY_REPORT_DIR: ${{ runner.temp }}/install-report
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
if: ${{ always() }}
with:
name: cli-install-${{ matrix.os }}-${{ matrix.node }}
path: ${{ runner.temp }}/install-report/
retention-days: 14
publish:
needs: [build, install]
if: github.event_name == 'push'
permissions:
contents: write
runs-on: ubuntu-latest
timeout-minutes: 5
env:
MCODE_RELEASE_TAG: ${{ needs.build.outputs.tag }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
package-manager-cache: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: cli-package
path: ${{ runner.temp }}/package
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: cli-install-*
path: ${{ runner.temp }}/reports
- name: Publish only the authenticated, validated archive
run: node scripts/publish-cli-release.mjs
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
MCODE_RELEASE_DIRECTORY: ${{ runner.temp }}/package
MCODE_RELEASE_REPORTS: ${{ runner.temp }}/reports
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Existing README files, `CONTRIBUTING.md`, `.github/PULL_REQUEST_TEMPLATE.md`, Ma

`Node compatibility` runs weekly and on demand against macOS and Linux with Node 22.19.0, 24.2.0, 25 and 26. Windows is also temporarily excluded from this matrix. It does not run automatically on PRs. Dispatch it on the selected branch for changes to supported Node versions, native dependencies or compatibility-sensitive verification tooling, and before a source release. This covers the minimum versions of the two supported ranges and the additional supported majors. Deferring those versions from ordinary PR checks can delay regression discovery; a known failure in a supported version still needs resolution before release. Dependabot proposes weekly Actions and npm updates, grouping Actions and development-tool minor/patch updates. External Actions use reviewed full commit SHAs, while local actions and reusable workflows come from the same checked-out revision.

Source candidates are requested independently through the `Source candidate` workflow; ordinary PRs and main pushes do not produce them. Its Linux/macOS archive validation is described in [Releasing](docs/releasing.md). Product npm and installer publication remains a separate release process.
Source candidates are requested independently through the `Source candidate` workflow; ordinary PRs and main pushes do not produce them. Its Linux/macOS archive validation is described in [Releasing](docs/releasing.md). `CLI release` builds npm-installable tar.gz packages from version tags and validates the same archive before attaching it to a GitHub Release. Its `package` verification profile tests installation of an existing archive; it does not replace full source verification. npm registry and official installer publication remain separate release processes.

Source export reports separate archive creation, extraction, inventory validation, hashing, and cleanup timings in `export.json`. Windows uses native `tar` after complete archive preflight; other systems use the Node extractor. The preflight rejects traversal, links, duplicate entries and Git metadata before writing files. A machine without native `tar` falls back to Node. Set `MCODE_SOURCE_EXTRACTOR=node` or `native` when comparing extractors locally.

Expand Down
33 changes: 32 additions & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,35 @@
# Install from source
# Installation packages and source builds

## Install a GitHub release archive

When a CLI release is available on [GitHub Releases](https://github.com/MiniMax-AI/minimax-code/releases),
download `minimax-code-X.Y.Z.tar.gz` and the matching `.sha256` file. This is an npm
installation package containing the built CLI; no source build or pnpm is needed.
Install Node.js 22.19+ (22.x), 24.2+ (24.x), 25 or 26 first. npm still needs network
access to public npm for runtime dependencies. Native dependencies can require
Python and C/C++ build tools when no matching prebuilt binary is available.

For example, for an available `v0.4.13` release:

```bash
# Linux; on macOS use: shasum -a 256 -c minimax-code-0.4.13.tar.gz.sha256
sha256sum -c minimax-code-0.4.13.tar.gz.sha256
npm install --global ./minimax-code-0.4.13.tar.gz --registry=https://registry.npmjs.org/ --include=optional --ignore-scripts=false --allow-scripts=better-sqlite3
mcode --version
```

Keep optional dependencies enabled and allow the native SQLite installation
script. The tag determines the installed version. GitHub archive installation is
validated on Linux and macOS; Windows package acceptance is currently not run.

This archive uses the same `@minimax-ai/code` package name, `mcode` command and
default user data directory as the official npm CLI. Installing it globally into
the same npm prefix replaces that npm installation. Update to another GitHub
version by explicitly installing its archive; the built-in updater follows the
official npm registry channel and does not select GitHub release assets. To remove
the package, use `npm uninstall --global @minimax-ai/code`. User data remains in place.

## Install from source

The official CLI is available as [`@minimax-ai/code`](https://www.npmjs.com/package/@minimax-ai/code). Public npm `latest` was 0.4.12 on 2026-09-18. Follow the [official quick start](https://agent.minimax.io/docs/cli/quick-start) or the [README installation steps](../README.md#quick-start) for the macOS / Linux / WSL installer, Windows PowerShell installer, or npm installation.

Expand Down
72 changes: 71 additions & 1 deletion docs/releasing.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,74 @@
# Releasing a source preview
# Releasing MiniMax Code

## Tag-triggered CLI installation packages

Run the release command from a clean checkout of the latest reviewed `origin/main`.
Git and an authenticated `gh-axi` or `gh` are required:

```bash
pnpm release:cli --version 0.4.13 --dry-run
pnpm release:cli --version 0.4.13
```

The dry run checks the starting revision, versions and remote refs without changing
files, creating commits or pushing. The release command then:

1. Creates `release/v0.4.13` from the reviewed starting commit.
2. Bumps `package.json` and `packages/tui/package.json` together and commits them.
3. Creates the annotated `v0.4.13` tag on that version commit.
4. Atomically pushes the release branch and tag, without pushing `main`.
5. Opens a version PR back to `main`; merge it through the normal review process.

CI requires the tag, both committed source versions and `mcode --version` to agree.
It does not override the source version during a build. An existing tag or release
branch, a non-increasing version, uncommitted files, or a starting commit other
than the latest `origin/main` stops the command before version changes.

The tag starts the release workflow independently of the version PR. If a network
or PR-creation failure occurs, inspect the local and remote branch/tag before
retrying: the version commit and tag are retained for recovery. If both refs were
pushed but opening the PR failed, open that version PR manually. Never delete and
recreate an already distributed tag. Merge the version PR before starting the
next release so `main` carries the released version.

The workflow runs the full verification profile and secret scans, builds one
`minimax-code-X.Y.Z.tar.gz` npm installation package, and authenticates and installs
that same archive on Linux and macOS with Node 22.19.0, 24.2.0, 25 and 26. Each
installation checks the generated `mcode` launcher, native SQLite, ripgrep, and
the offline smoke/BYOK suites. Windows validation remains paused.

Only after every installation succeeds does CI create a GitHub Release with the
archive and its `.sha256` checksum. Tags such as `v0.4.13-rc.1` create prereleases.
Release creation starts as a draft; assets are uploaded before it becomes public.
Existing releases are never overwritten. If publication fails after draft
creation, inspect the draft and workflow artifacts before deciding whether to
finish publication manually or delete only the incomplete draft and rerun.
Never move an already distributed tag to different code.

To exercise this workflow without publication, dispatch `CLI release` on a
selected branch. An optional tag input must match the committed source version. A manual dispatch only builds and
validates Actions artifacts, even when the requested tag already exists.
PRs that change release tooling also run the build/install matrix using the
committed source version, without creating a tag or publishing a release.

To reproduce the packaging and installation checks locally, use a clean reviewed
commit and keep output outside the repository:

```bash
export MCODE_RELEASE_TAG="v$(node -p 'require("./package.json").version')"
pnpm verify
node scripts/package-cli-release.mjs "$MCODE_RELEASE_TAG" /tmp/mcode-release
MCODE_RELEASE_ARCHIVE="/tmp/mcode-release/minimax-code-${MCODE_RELEASE_TAG#v}.tar.gz" pnpm verify --profile package
```

The `package` profile validates installation of an existing archive; it does not
replace full source verification. The archive includes the compiled CLI, runtime
assets, licenses and a `release.json` source receipt. npm installs external runtime
dependencies, including native dependencies, for the user's platform. It does not
include Node.js and is not an offline bundle. See [installation](installation.md#install-a-github-release-archive).
This workflow does not publish to the npm registry or change the official installer.

## Source previews

The current source target is MiniMax Code 0.4.12. Workspace and local-build manifests remain `private: true` to prevent accidental npm publication. A source release, npm package, and installer are separate artifacts with separate verification.

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"scripts": {
"build": "node scripts/build.mjs",
"release:cli": "node scripts/release-cli.mjs",
"start": "node dist/cli.js",
"mcode": "node dist/cli.js",
"verify": "node scripts/verify.mjs",
Expand Down
6 changes: 6 additions & 0 deletions release/public-source.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 10 additions & 8 deletions scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
writeFileSync,
} from "node:fs";
import { createRequire } from "node:module";
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from "node:url";
import path from "node:path";
import { copyLocalRuntimeAssets } from "./lib/local-runtime-assets.mjs";
Expand All @@ -17,6 +18,7 @@ import { shouldCopyTuiRuntimeResource } from "./lib/tui-package-privacy.mjs";
import { TUI_DISABLED_BUILTIN_SKILL_NAMES } from "./lib/builtin-skills.mjs";
import { copyMcodeToolsArtifact } from './lib/mcode-tools-artifact.mjs';
import { readExtraction } from "./lib/release-metadata.mjs";
import { cliBuildVersion, cliExternalModules } from './lib/cli-release.mjs';

const root = fileURLToPath(new URL("../", import.meta.url));
const metadata = readExtraction(root);
Expand Down Expand Up @@ -72,7 +74,7 @@ const sourcePlugin = {
});
},
};
const version = packages.get("@minimax/code").manifest.version;
const version = cliBuildVersion(root);
const result = await build({
absWorkingDir: root,
entryPoints: {
Expand All @@ -81,12 +83,7 @@ const result = await build({
'mcode-tools': 'packages/tui/src/cli/mcode-tools-entry.ts',
'matrix-mcp-stdio': 'packages/agent-tools/src/desktop/matrix-mcp-stdio.ts',
},
external: [
"better-sqlite3",
"@mariozechner/clipboard",
"@vscode/ripgrep",
"@larksuiteoapi/node-sdk",
],
external: cliExternalModules,
outdir,
bundle: true,
splitting: true,
Expand Down Expand Up @@ -143,7 +140,12 @@ console.log(
writeFileSync(
path.join(outdir, "package.json"),
JSON.stringify(
{ name: "@minimax-ai/code", version, type: "module", private: true },
{
name: "@minimax-ai/code", version, type: "module", private: true,
...(process.env.MCODE_RELEASE_TAG ? {
gitHead: execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(),
} : {}),
},
null,
2,
) + "\n",
Expand Down
Loading
Loading