diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 00000000..9ac50133 --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,11 @@ +[profile.ci] +fail-fast = false +# A passing retry must never hide a failure. Repeated runs are recorded separately. +retries = 0 +slow-timeout = { period = "60s", terminate-after = 5 } +failure-output = "immediate-final" + +[profile.ci.junit] +path = "junit.xml" +store-success-output = false +store-failure-output = false diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..dca633df --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,13 @@ +FROM rust:1.98.0-bookworm +RUN apt-get update && apt-get install -y --no-install-recommends \ + libasound2-dev libssl-dev pkg-config python3 python3-venv ripgrep git ca-certificates \ + && rm -rf /var/lib/apt/lists/* +RUN useradd --create-home --shell /bin/bash cortex \ + && mkdir -p /home/cortex/.cargo /home/cortex/.rustup \ + && cp -a /usr/local/rustup/. /home/cortex/.rustup/ \ + && chown -R cortex:cortex /home/cortex +ENV RUSTUP_HOME=/home/cortex/.rustup \ + CARGO_HOME=/home/cortex/.cargo +ENV PATH=/usr/local/cargo/bin:/home/cortex/.cargo/bin:${PATH} +USER cortex +WORKDIR /workspaces/cli diff --git a/.devcontainer/Dockerfile.dockerignore b/.devcontainer/Dockerfile.dockerignore new file mode 100644 index 00000000..8ce84904 --- /dev/null +++ b/.devcontainer/Dockerfile.dockerignore @@ -0,0 +1,3 @@ +** +!.devcontainer/ +!.devcontainer/Dockerfile diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..a8f52863 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,16 @@ +{ + "name": "Cortex Rust workspace", + "build": {"dockerfile": "Dockerfile", "context": ".."}, + "remoteUser": "cortex", + "updateRemoteUserUID": true, + "postCreateCommand": "bash scripts/dev-setup.sh", + "containerEnv": { + "CORTEX_HOME": "/home/cortex/.cortex", + "CARGO_BUILD_JOBS": "4" + }, + "customizations": { + "vscode": { + "extensions": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml"] + } + } +} diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..cb9443ad --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# Names only. Do not commit real credentials or put them in shell history. +# The local QA runner creates its own isolated environment and server credential. +# CORTEX_HOME= +# CORTEX_DIAGNOSTICS_DIR= +# CORTEX_SERVER_API_KEY= +# CORTEX_JWT_SECRET= +# CORTEX_API_KEY= +# CORTEX_LISTEN_ADDR=127.0.0.1:55554 diff --git a/.factory/skills/local-qa/SKILL.md b/.factory/skills/local-qa/SKILL.md new file mode 100644 index 00000000..01135fe0 --- /dev/null +++ b/.factory/skills/local-qa/SKILL.md @@ -0,0 +1,22 @@ +--- +name: local-qa +description: Exercise the Cortex CLI and real loopback app server with isolated data and negative security cases, without contacting the coding service. +--- + +# Local functional QA + +Read `docs/guides/development.md` and `docs/reference/app-server.md`. + +Build the affected binaries with the locked graph, then run +`python3 scripts/readiness/qa.py`. This interacts with actual application +processes and local HTTP handlers; it does not fake model responses. + +Review `target/readiness/qa/report.json` and the sanitized aggregate insights. +Report each completed flow and any failing assertion. A missing binary or +startup failure is blocked/failed evidence, never success. + +For changes to the interactive TUI, this script is insufficient. Use the +existing headless tests and an available terminal interaction skill with a +dedicated approved test account. Do not invent credentials or bypass login. +If interactive or live API QA cannot run, say which flow is blocked and why. +Keep captures local. Do not publish evidence or create issues automatically. diff --git a/.factory/skills/verify-readiness/SKILL.md b/.factory/skills/verify-readiness/SKILL.md new file mode 100644 index 00000000..b6005e83 --- /dev/null +++ b/.factory/skills/verify-readiness/SKILL.md @@ -0,0 +1,19 @@ +--- +name: verify-readiness +description: Verify Cortex source policy, dependency drift, generated contracts, and local test evidence before proposing a merge. +--- + +# Verify readiness + +Read `AGENTS.md`, `.rules/testing.md`, `docs/guides/development.md`, and +`docs/guides/quality.md`. Work only in this repository. + +1. Inspect the diff and preserve unrelated work. +2. Resolve the real target branch/base commit. Never use HEAD as a regression + baseline just to pass the gate. +3. Run the narrow relevant Rust tests, policy unit tests, source/dependency + checks, schema freshness, and coverage as documented. +4. Retain reports under `target/readiness/`. Report exit statuses, failed tests, + existing debt, and unverified platforms. Do not replace failures with skips. +5. Do not commit, push, change GitHub settings, release, or post reports without + an explicit request. Never attach raw sessions, debug logs, or credentials. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..687881df --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Verified repository administrator. Additional owners require their agreement. +* @echobt diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7a978d47..f6794b99 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,6 +9,8 @@ - [ ] `cargo test --workspace` (or note the subset and why) - [ ] TUI / snapshot tests for every surface this PR touches - [ ] `cargo audit` (or note a documented `.cargo/audit.toml` exception) +- [ ] Source/dependency policy and generated API contracts verified against the real PR base +- [ ] Local functional/security QA and changed-line coverage passed (or explain the blocker) ## Attestation (required) @@ -23,3 +25,5 @@ I attest that: ## Risk + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..48ead41f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + cooldown: + default-days: 7 + open-pull-requests-limit: 5 + labels: [dependencies, "area:build"] + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + labels: [dependencies, "area:build"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ad8ec58..359b00d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,41 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: + quality: + name: Source and dependency policy + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + QUALITY_BASE: ${{ github.event.pull_request.base.sha || github.event.before }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - uses: dtolnay/rust-toolchain@1.98.0 + with: + components: rustfmt, clippy + - uses: taiki-e/install-action@v2 + with: + tool: cargo-machete@0.9.1 + - run: python -m pip install -r scripts/readiness/requirements.txt + - run: python -B -m unittest discover -s scripts/readiness -p 'test_*.py' + - run: python scripts/readiness/quality.py --base "$QUALITY_BASE" + - run: cargo machete --with-metadata + - run: python scripts/readiness/release_age.py --base "$QUALITY_BASE" + - uses: actions/upload-artifact@v4 + if: always() + with: + name: source-quality + path: target/readiness/quality.json + retention-days: 14 + version-check: name: CLI Version Check runs-on: ubuntu-latest @@ -67,13 +101,71 @@ jobs: - name: Install Linux dependencies run: | sudo apt-get update - sudo apt-get install -y libasound2-dev libssl-dev pkg-config + sudo apt-get install -y libasound2-dev libssl-dev pkg-config git ripgrep - uses: dtolnay/rust-toolchain@1.98.0 - uses: Swatinem/rust-cache@v2 with: prefix-key: rust-test + - uses: taiki-e/install-action@v2 + with: + tool: cargo-nextest@0.9.102 - name: Run tests - run: cargo test --workspace + run: python3 scripts/readiness/tests.py + - name: Run documentation tests + run: cargo test --locked --workspace --doc + - name: Verify generated API contracts + run: python3 scripts/readiness/schema.py + - name: Build local QA applications + run: cargo build --locked -p cortex-cli -p cortex-app-server + - name: Local functional QA and dynamic security tests + run: python3 scripts/readiness/qa.py + - uses: actions/upload-artifact@v4 + if: always() + with: + name: test-performance + path: target/readiness/tests/ + retention-days: 30 + - uses: actions/upload-artifact@v4 + if: always() + with: + name: local-qa + # Diagnostic journals and aggregates remain local to the runner. + path: target/readiness/qa/report.json + retention-days: 14 + + coverage: + name: Changed-line coverage + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + QUALITY_BASE: ${{ github.event.pull_request.base.sha || github.event.before }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - run: sudo apt-get update && sudo apt-get install -y libasound2-dev libssl-dev pkg-config git ripgrep + - uses: dtolnay/rust-toolchain@1.98.0 + with: + components: llvm-tools-preview + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rust-coverage + - uses: taiki-e/install-action@v2 + with: + tool: cargo-llvm-cov@0.6.21,cargo-nextest@0.9.102 + - name: Collect application coverage + run: | + mkdir -p target/readiness + cargo llvm-cov nextest --locked -p cortex-cli -p cortex-app-server -p cortex-common --profile ci --lcov --output-path target/readiness/lcov.info + - run: python3 scripts/readiness/coverage.py --base "$QUALITY_BASE" + - uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage + path: | + target/readiness/lcov.info + target/readiness/coverage.json + retention-days: 14 tui: name: TUI checks @@ -107,14 +199,14 @@ jobs: - uses: dtolnay/rust-toolchain@1.98.0 - uses: taiki-e/install-action@v2 with: - tool: cargo-audit + tool: cargo-audit@0.22.2 - name: Audit Rust dependencies run: cargo audit ci-success: name: CI Success runs-on: ubuntu-latest - needs: [version-check, fmt, clippy, test, tui, audit] + needs: [version-check, fmt, clippy, test, tui, audit, quality, coverage] if: always() steps: - name: Check all jobs @@ -124,7 +216,9 @@ jobs: "${{ needs.clippy.result }}" != "success" || \ "${{ needs.test.result }}" != "success" || \ "${{ needs.tui.result }}" != "success" || \ - "${{ needs.audit.result }}" != "success" ]]; then + "${{ needs.audit.result }}" != "success" || \ + "${{ needs.quality.result }}" != "success" || \ + "${{ needs.coverage.result }}" != "success" ]]; then echo "One or more required CI jobs failed" exit 1 fi diff --git a/.github/workflows/test-stability.yml b/.github/workflows/test-stability.yml new file mode 100644 index 00000000..b57f8fdc --- /dev/null +++ b/.github/workflows/test-stability.yml @@ -0,0 +1,26 @@ +name: Test stability +on: + schedule: + - cron: '30 5 * * 1' + workflow_dispatch: +permissions: + contents: read +jobs: + repeat: + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - run: sudo apt-get update && sudo apt-get install -y libasound2-dev libssl-dev pkg-config git ripgrep + - uses: dtolnay/rust-toolchain@1.98.0 + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: + tool: cargo-nextest@0.9.102 + - run: python3 scripts/readiness/tests.py --repeat 3 + - uses: actions/upload-artifact@v4 + if: always() + with: + name: test-stability + path: target/readiness/tests/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index fdf96bd0..398fccd5 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ Thumbs.db .cargo-home/ .sccache/ .rustup/ +__pycache__/ +*.py[cod] diff --git a/.quality/dependency-compatibility.json b/.quality/dependency-compatibility.json new file mode 100644 index 00000000..0f2885fe --- /dev/null +++ b/.quality/dependency-compatibility.json @@ -0,0 +1,66 @@ +{ + "src/cortex-agents/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-app-server/Cargo.toml:dependencies:dirs": { + "declaration": "5", + "reason": "Retains dirs 5 platform path behavior until its consumers are migrated and platform-tested." + }, + "src/cortex-batch/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-compact/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-engine/Cargo.toml:dependencies:rand": { + "declaration": "0.8", + "reason": "Retains rand 0.8 Rng/generation APIs; upgrade requires a separate compatibility change." + }, + "src/cortex-ghost/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-hooks/Cargo.toml:dependencies:dirs": { + "declaration": "5.0", + "reason": "Retains dirs 5 platform path behavior until its consumers are migrated and platform-tested." + }, + "src/cortex-hooks/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-linux-sandbox/Cargo.toml:target.cfg(target_os = \"linux\").dependencies:seccompiler": { + "declaration": "0.4", + "reason": "Retains seccompiler 0.4 sandbox filter API; changing sandbox policy needs a dedicated compatibility review." + }, + "src/cortex-resume/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-review/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-share/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-snapshot/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-storage/Cargo.toml:dependencies:dirs": { + "declaration": "5", + "reason": "Retains dirs 5 platform path behavior until its consumers are migrated and platform-tested." + }, + "src/cortex-storage/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + }, + "src/cortex-windows-sandbox/Cargo.toml:dependencies:thiserror": { + "declaration": "1", + "reason": "Retains the existing thiserror 1 derive API until its consumers are migrated together." + } +} diff --git a/AGENTS.md b/AGENTS.md index ffed3d86..2fd3dacb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,6 +68,15 @@ Match CI (`.github/workflows/ci.yml`): - TUI job for the framework + app surfaces - `./scripts/check-cli-version.sh` - PR attestation checklist in `.github/PULL_REQUEST_TEMPLATE.md` +- Source/dependency policy, API-contract freshness, local QA, and changed-line + coverage in CI. Use the actual PR base SHA, never a self-baseline. + +Local setup and the exact reporting/coverage commands: +[`docs/guides/development.md`](docs/guides/development.md). +Policy and existing-debt handling: +[`docs/guides/quality.md`](docs/guides/quality.md). +Diagnostics stay local, opt-in, and content-free: +[`docs/guides/operations.md`](docs/guides/operations.md). ## Where to read what diff --git a/Cargo.lock b/Cargo.lock index bb3eec32..281fe929 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,6 +330,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ "aws-lc-sys", + "untrusted 0.7.1", "zeroize", ] @@ -819,17 +820,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "console" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" -dependencies = [ - "encode_unicode", - "libc", - "windows-sys 0.61.2", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -972,13 +962,15 @@ dependencies = [ "notify", "notify-debouncer-mini", "reqwest 0.13.4", + "schemars", "serde", "serde_json", "serde_yaml", + "tempfile", "thiserror 2.0.20", "tokio", "tokio-stream", - "tokio-test", + "tower", "tower-http", "tracing", "tracing-subscriber", @@ -1068,7 +1060,6 @@ dependencies = [ "serde", "thiserror 2.0.20", "tokio", - "tokio-test", "uuid", ] @@ -1105,6 +1096,7 @@ dependencies = [ "thiserror 2.0.20", "tokio", "toml 0.8.23", + "uuid", "windows-sys 0.52.0", ] @@ -1137,7 +1129,6 @@ dependencies = [ "serde", "serde_json", "tokio", - "tokio-test", "tracing", "tree-sitter-bash", "tui-textarea", @@ -1183,18 +1174,14 @@ dependencies = [ "futures", "glob", "hostname", - "insta", "keyring", - "landlock", "libc", "mdns-sd", "num_cpus", "once_cell", - "pretty_assertions", "rand 0.8.8", "regex", "reqwest 0.13.4", - "seccompiler 0.5.0", "secrecy", "serde", "serde_json", @@ -1207,7 +1194,6 @@ dependencies = [ "thiserror 2.0.20", "tokio", "tokio-stream", - "tokio-test", "tokio-util", "toml 0.8.23", "tracing", @@ -1216,7 +1202,6 @@ dependencies = [ "uuid", "walkdir", "which", - "wiremock", "zeroize", ] @@ -1260,7 +1245,6 @@ dependencies = [ "cortex-engine", "cortex-protocol", "serde_json", - "tempfile", "tracing-subscriber", "uuid", ] @@ -1275,7 +1259,6 @@ dependencies = [ "tempfile", "thiserror 2.0.20", "tokio", - "tokio-test", "tracing", ] @@ -1339,10 +1322,8 @@ dependencies = [ "reqwest 0.13.4", "serde", "serde_json", - "tempfile", "thiserror 2.0.20", "tokio", - "tokio-test", "tracing", "which", "wiremock", @@ -1367,7 +1348,6 @@ dependencies = [ "serde", "serde_json", "sha2", - "tempfile", "thiserror 2.0.20", "tokio", "tracing", @@ -1450,7 +1430,6 @@ dependencies = [ "serde", "thiserror 2.0.20", "tokio", - "tokio-test", ] [[package]] @@ -1471,7 +1450,6 @@ dependencies = [ name = "cortex-plugins" version = "0.1.7" dependencies = [ - "anyhow", "async-trait", "chrono", "cortex-common", @@ -1486,7 +1464,6 @@ dependencies = [ "tempfile", "thiserror 2.0.20", "tokio", - "tokio-test", "toml 0.8.23", "tracing", "url", @@ -1507,26 +1484,21 @@ version = "0.1.7" dependencies = [ "chrono", "indexmap", - "pretty_assertions", "serde", "serde_json", "thiserror 2.0.20", - "tokio", ] [[package]] name = "cortex-protocol" version = "0.1.7" dependencies = [ - "anyhow", "base64", "chrono", - "pretty_assertions", "schemars", "serde", "serde_json", "strum_macros 0.27.2", - "tempfile", "uuid", ] @@ -1597,10 +1569,8 @@ dependencies = [ "dirs 6.0.0", "serde", "serde_json", - "tempfile", "thiserror 2.0.20", "tokio", - "tokio-test", "tracing", "uuid", ] @@ -1636,14 +1606,11 @@ dependencies = [ "secrecy", "serde", "serde_json", - "tempfile", "thiserror 2.0.20", "tokio", - "tokio-test", "tokio-tungstenite 0.24.0", "tracing", "urlencoding", - "wiremock", ] [[package]] @@ -1712,7 +1679,6 @@ dependencies = [ "thiserror 2.0.20", "tokio", "tokio-stream", - "tokio-test", "toml 0.8.23", "tracing", "unicode-segmentation", @@ -1737,7 +1703,6 @@ version = "0.1.7" dependencies = [ "anyhow", "chrono", - "cortex-core", "cortex-tui-buffer", "cortex-tui-components", "cortex-tui-core", @@ -1748,7 +1713,6 @@ dependencies = [ "tempfile", "thiserror 2.0.20", "tokio", - "tokio-test", "tracing", "uuid", ] @@ -1760,9 +1724,7 @@ dependencies = [ "cortex-core", "crossterm", "ratatui", - "tokio-test", "unicode-segmentation", - "unicode-width", ] [[package]] @@ -1813,7 +1775,6 @@ dependencies = [ "cortex-tui-text", "once_cell", "parking_lot", - "pretty_assertions", "streaming-iterator", "thiserror 2.0.20", "tree-sitter", @@ -1861,7 +1822,6 @@ dependencies = [ "futures", "getrandom 0.2.17", "hex", - "libc", "reqwest 0.13.4", "self-replace", "serde", @@ -1871,7 +1831,6 @@ dependencies = [ "tempfile", "thiserror 2.0.20", "tokio", - "tokio-test", "tracing", "windows-sys 0.59.0", "wiremock", @@ -1923,7 +1882,6 @@ version = "0.1.0" dependencies = [ "serde", "thiserror 1.0.69", - "tokio-test", "tracing", "windows 0.58.0", ] @@ -2477,12 +2435,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - [[package]] name = "digest" version = "0.10.7" @@ -2685,12 +2637,6 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - [[package]] name = "encoding_rs" version = "0.8.35" @@ -3262,6 +3208,16 @@ dependencies = [ "serde_core", ] +[[package]] +name = "hdrhistogram" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d1053f4708f0af3cf9fc5bffc7e68a914a3c45becb231c80068c9c3f78bea" +dependencies = [ + "byteorder", + "num-traits", +] + [[package]] name = "heck" version = "0.5.0" @@ -3660,18 +3616,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "insta" -version = "1.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0f8fee8c926415c58d6ae43a08523a26faccb2323f5e6b644fe7dd4ef6b82" -dependencies = [ - "console", - "once_cell", - "similar", - "tempfile", -] - [[package]] name = "instability" version = "0.3.13" @@ -3842,6 +3786,7 @@ version = "10.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" dependencies = [ + "aws-lc-rs", "base64", "getrandom 0.2.17", "js-sys", @@ -5223,16 +5168,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "pretty_assertions" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" -dependencies = [ - "diff", - "yansi", -] - [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -5819,7 +5754,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -5953,7 +5888,7 @@ dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -6992,17 +6927,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-test" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6d24790a10a7af737693a3e8f1d03faef7e6ca0cc99aae5066f533766de545" -dependencies = [ - "futures-core", - "tokio", - "tokio-stream", -] - [[package]] name = "tokio-tungstenite" version = "0.24.0" @@ -7197,9 +7121,13 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", + "hdrhistogram", + "indexmap", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -7218,6 +7146,7 @@ dependencies = [ "futures-util", "http", "http-body", + "http-body-util", "pin-project-lite", "tokio", "tokio-util", @@ -7485,6 +7414,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -8853,12 +8788,6 @@ dependencies = [ "lzma-sys", ] -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - [[package]] name = "yoke" version = "0.8.3" diff --git a/Cargo.toml b/Cargo.toml index 2217b79b..7629f166 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -437,6 +437,11 @@ panic = "abort" strip = "symbols" opt-level = 3 +[profile.profiling] +inherits = "release" +debug = 1 +strip = "none" + [profile.release.build-override] opt-level = 3 diff --git a/README.md b/README.md index 6e0fc8e4..3a2a4239 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,12 @@ The index is at [docs/README.md](./docs/README.md). ## Building and testing +For native prerequisites, pinned tools, a development container, real local QA, +test reports, and coverage, follow +[Development and local QA](./docs/guides/development.md). + ```bash -cargo build -p cortex-cli +cargo build --locked -p cortex-cli ./target/debug/Cortex --help ``` diff --git a/cortex-tui-framework/Cargo.toml b/cortex-tui-framework/Cargo.toml index 65d7eadd..411ed61e 100644 --- a/cortex-tui-framework/Cargo.toml +++ b/cortex-tui-framework/Cargo.toml @@ -12,14 +12,14 @@ authors.workspace = true workspace = true [dependencies] -cortex-tui-core = { path = "crates/cortex-tui-core" } -cortex-tui-buffer = { path = "crates/cortex-tui-buffer" } -cortex-tui-terminal = { path = "crates/cortex-tui-terminal" } -cortex-tui-layout = { path = "crates/cortex-tui-layout" } -cortex-tui-widgets = { path = "crates/cortex-tui-widgets" } -cortex-tui-input = { path = "crates/cortex-tui-input" } -cortex-tui-text = { path = "crates/cortex-tui-text" } -cortex-tui-syntax = { path = "crates/cortex-tui-syntax" } +cortex-tui-core = { workspace = true } +cortex-tui-buffer = { workspace = true } +cortex-tui-terminal = { workspace = true } +cortex-tui-layout = { workspace = true } +cortex-tui-widgets = { workspace = true } +cortex-tui-input = { workspace = true } +cortex-tui-text = { workspace = true } +cortex-tui-syntax = { workspace = true } # Workspace member of CortexLM/cli diff --git a/cortex-tui-framework/crates/cortex-tui-buffer/Cargo.toml b/cortex-tui-framework/crates/cortex-tui-buffer/Cargo.toml index a92efd5f..92328dec 100644 --- a/cortex-tui-framework/crates/cortex-tui-buffer/Cargo.toml +++ b/cortex-tui-framework/crates/cortex-tui-buffer/Cargo.toml @@ -10,5 +10,5 @@ description = "Double-buffered terminal rendering for Cortex TUI" workspace = true [dependencies] -cortex-tui-core = { path = "../cortex-tui-core" } +cortex-tui-core = { workspace = true } smallvec = { workspace = true } diff --git a/cortex-tui-framework/crates/cortex-tui-syntax/Cargo.toml b/cortex-tui-framework/crates/cortex-tui-syntax/Cargo.toml index d3e07c66..4b29506f 100644 --- a/cortex-tui-framework/crates/cortex-tui-syntax/Cargo.toml +++ b/cortex-tui-framework/crates/cortex-tui-syntax/Cargo.toml @@ -10,13 +10,10 @@ description = "Syntax highlighting for Cortex TUI" workspace = true [dependencies] -cortex-tui-text = { path = "../cortex-tui-text" } +cortex-tui-text = { workspace = true } tree-sitter = { workspace = true } streaming-iterator = "0.1" ahash = { workspace = true } thiserror = { workspace = true } parking_lot = { workspace = true } -once_cell = "1.20" - -[dev-dependencies] -pretty_assertions = "1.4" +once_cell = { workspace = true } diff --git a/cortex-tui-framework/crates/cortex-tui-terminal/Cargo.toml b/cortex-tui-framework/crates/cortex-tui-terminal/Cargo.toml index 0bebe4f3..1fa9b0f2 100644 --- a/cortex-tui-framework/crates/cortex-tui-terminal/Cargo.toml +++ b/cortex-tui-framework/crates/cortex-tui-terminal/Cargo.toml @@ -10,7 +10,7 @@ description = "Terminal backend for Cortex TUI" workspace = true [dependencies] -cortex-tui-core = { path = "../cortex-tui-core" } -cortex-tui-buffer = { path = "../cortex-tui-buffer" } -cortex-tui-input = { path = "../cortex-tui-input" } +cortex-tui-core = { workspace = true } +cortex-tui-buffer = { workspace = true } +cortex-tui-input = { workspace = true } crossterm = { workspace = true } diff --git a/cortex-tui-framework/crates/cortex-tui-widgets/Cargo.toml b/cortex-tui-framework/crates/cortex-tui-widgets/Cargo.toml index 0351f8d8..010eb08e 100644 --- a/cortex-tui-framework/crates/cortex-tui-widgets/Cargo.toml +++ b/cortex-tui-framework/crates/cortex-tui-widgets/Cargo.toml @@ -10,8 +10,8 @@ description = "Widget library for Cortex TUI" workspace = true [dependencies] -cortex-tui-core = { path = "../cortex-tui-core" } -cortex-tui-buffer = { path = "../cortex-tui-buffer" } +cortex-tui-core = { workspace = true } +cortex-tui-buffer = { workspace = true } unicode-segmentation = { workspace = true } unicode-width = { workspace = true } smallvec = { workspace = true } diff --git a/docs/README.md b/docs/README.md index e4d3b2a2..5a9388bc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,6 +21,8 @@ New here? Start with **[Getting started](guides/getting-started.md)**, then keep | [Sessions](guides/sessions.md) | Resume, list, export, import, share, protect | | [Headless / exec mode](guides/exec.md) | Non-interactive runs for scripts and CI | | [Plan and Spec modes](guides/plan.md) | Get a plan approved before anything is written | +| [Development and local QA](guides/development.md) | Reproducible setup, real local tests, coverage and test reports | +| [Source quality](guides/quality.md) | Dependency, complexity, duplication, feature-flag and contract gates | ### Configuration @@ -50,6 +52,8 @@ New here? Start with **[Getting started](guides/getting-started.md)**, then keep | [Slash commands](reference/slash-commands.md) | Everything you can type after `/` in the TUI | | [Keyboard shortcuts](reference/keyboard.md) | Key bindings by context | | [Signing in](reference/login.md) | Browser, device-code, SSO and token sign-in; the keyring | +| [Local app-server API](reference/app-server.md) | Authentication, supported OpenAPI contract and local readiness | +| [Privacy](reference/privacy.md) | Local diagnostic allowlist, consent, retention and data boundaries | ### Operations @@ -58,6 +62,9 @@ New here? Start with **[Getting started](guides/getting-started.md)**, then keep | [Troubleshooting](troubleshooting.md) | Common failures and how to diagnose them | | [Contributing](CONTRIBUTING.md) | Filing issues, PR conventions, required checks | | [CI secrets](CI_SECRETS.md) | Secret *names* the release workflows expect | +| [Local operations](guides/operations.md) | Alerts, error investigation, deployment comparison and profiling | +| [Maintenance](guides/maintenance.md) | Ownership, labels, backlog triage and release review | +| [Readiness remediation](guides/readiness-remediation.md) | Local validation snapshot and explicit remaining limits | ## Conventions used here diff --git a/docs/guides/development.md b/docs/guides/development.md new file mode 100644 index 00000000..558780ea --- /dev/null +++ b/docs/guides/development.md @@ -0,0 +1,93 @@ +# Reproducible local development + +## Prerequisites and setup + +Use the Rust version in `rust-toolchain.toml` (1.98.0). On Debian/Ubuntu: + +```bash +sudo apt-get update +sudo apt-get install -y build-essential libasound2-dev libssl-dev pkg-config python3-venv ripgrep git +bash scripts/dev-setup.sh +export PATH="$PWD/target/readiness-tools/bin:$PATH" +``` + +Setup fetches the **locked** graph, including the Git-sourced terminal dependency, +and installs pinned analysis/test tools under ignored `target/`. It does not +change Git configuration, install hooks, run a remote installer, or log in. +An offline build needs a successful `cargo fetch --locked` first. + +The development container uses the same prerequisites and setup command. +Open this repository with a Dev Containers-compatible editor and choose +**Reopen in Container**. It runs as the unprivileged `cortex` user and does not +mount credentials or Docker sockets. No ports are automatically published. +The workspace must be writable by that user (the editor maps the host UID on Linux). + +```bash +cargo build --locked -p cortex-cli -p cortex-app-server +./target/debug/Cortex debug doctor --json +python3 scripts/readiness/qa.py +``` + +No database or fake model service is required. Legacy REST sessions are in memory; +other session implementations use JSON/JSONL files. Local QA uses a fresh temporary +home/workspace, an ephemeral loopback port, and an in-memory generated server key. +It deletes only its own temporary data and stops its own server. + +## Real local QA + +`qa.py` exercises both built binaries, not mocks: + +- CLI: successful configuration/storage/tool checks, then invalid configuration + must fail with a nonzero exit status. +- Server: authenticated session creation, message storage/readback, deletion, + missing resources, request correlation, and metrics. +- Dynamic security: missing/invalid credentials, WebSocket upgrade protection, + endpoint-prefix bypass, oversized bodies, browser origins, workspace traversal, + and symlinks. + +Evidence is in `target/readiness/qa/`. These tests **do not generate a model response**. +Message storage is not evidence of a successful coding turn. + +`Cortex debug doctor` checks local prerequisites only. `coding_service: not_checked` +is deliberate. A failed tool, storage round-trip, or TOML parse fails the command. +Debug commands and `serve` do not start the automatic update check. + +For actual interactive TUI/chat QA, use the existing login procedure in +[Getting started](getting-started.md), with a dedicated user-approved test account. +Verify `/help`, a real turn, approvals, and disconnect recovery. Without such an +account, report that flow as **blocked**, not passed. Never substitute a success +response for the coding service. Keep live tests out of default CI. + +## Test reports and coverage + +```bash +python3 scripts/readiness/tests.py +python3 scripts/readiness/tests.py --repeat 3 +cargo test --locked --workspace --doc +mkdir -p target/readiness +cargo llvm-cov nextest --locked -p cortex-cli -p cortex-app-server -p cortex-common \ + --profile ci --lcov --output-path target/readiness/lcov.info +python3 scripts/readiness/coverage.py --base origin/main +``` + +Nextest 0.9.102 runs each test in an isolated process. Retries are disabled. +Each repetition has a separate JUnit report; any failure fails the command, +including a failure followed by a pass. Timings, slowest tests, and flaky test +names are in `target/readiness/tests/`. Weekly CI runs three repetitions. +Doctests still use Cargo because nextest does not run them. + +Coverage uses cargo-llvm-cov 0.6.21 and LLVM tools from the pinned Rust toolchain. +The enforced floor is **80% of changed executable lines** in the CLI, app server, +and shared common crate, under their `src/` directories. Test harnesses and +schema-export examples are validated separately, not included in that floor. +Missing coverage for a changed production file containing functions fails closed. +Existing unmodified uncovered lines are not counted as fixed. +Review the full LCOV artifact as well as the changed-line gate. + +Test names use `test_`; integration files live under the owning +crate's `tests/`. Assert observable outcomes and negative cases. Never ignore a +failure to make CI green. See [testing rules](../../.rules/testing.md). + +The append regression test checks immediate visibility after Tokio 1.53.1 file +writes. An awaited `flush` finishes the pending write; it is not an `fsync` +durability guarantee. Do not replace this check with sleeps or retries. diff --git a/docs/guides/maintenance.md b/docs/guides/maintenance.md new file mode 100644 index 00000000..d13e848b --- /dev/null +++ b/docs/guides/maintenance.md @@ -0,0 +1,41 @@ +# Maintenance and ownership + +`CODEOWNERS` names a verified repository administrator. Ownership changes need +the new owner's agreement. Main requires a pull request and at least one +independent approval; the author cannot satisfy their own approval requirement. +Do not bypass that rule to publish readiness work. + +## Weekly issue triage + +Use the existing type labels (`bug`, `enhancement`, `documentation`, +`dependencies`). Add one priority and at least one area after investigating: + +| Priority | Meaning | +| --- | --- | +| `priority:p0` | Active security/data-loss incident | +| `priority:p1` | Major user workflow blocked | +| `priority:p2` | Normal planned work | +| `priority:p3` | Low-impact improvement | + +Areas: `area:cli`, `area:tui`, `area:server`, `area:build`, `area:security`, +and `area:docs`. Do not assign a priority solely to satisfy a coverage metric. +Check new reports within two working days. Revisit issues without a maintainer +update for 30 days; close only with an explanation or a verified resolution, +not automatically because they are old. Link reproductions, owning code, and +fixes. Do not create artificial issues when the backlog is empty. + +## Weekly engineering/release review + +- Review source-quality artifacts and prioritize a real complexity, duplication, + or large-file hotspot. No-new-debt controls do not eliminate existing debt. +- Review feature-flag candidates. Decide whether to implement a tested consumer, + remove a retired flag, or document a genuine dynamic consumer. +- Inspect slowest/flaky-test reports. Reproduce and fix failures; never add + ignored tests or successful retries to conceal them. +- Review Dependabot and advisory findings. Preserve lockfiles and minimum-age + policy; security exceptions require evidence and review. +- Review actual completed release work. Use the existing release workflow when + there is a useful, tested release, not a calendar-driven empty version bump. + +Backlog health and deployment frequency are historical outcomes. A taxonomy, +runbook, or empty backlog is not proof that those outcomes have improved. diff --git a/docs/guides/operations.md b/docs/guides/operations.md new file mode 100644 index 00000000..ba0a109f --- /dev/null +++ b/docs/guides/operations.md @@ -0,0 +1,96 @@ +# Local operations and incident runbook + +## Opt-in local diagnostics + +Set `CORTEX_DIAGNOSTICS_DIR` to a private directory for either application. +If absent, no diagnostic journal is created. There is no OTLP, error-tracking, +analytics, or alert exporter in this implementation. + +```bash +export CORTEX_DIAGNOSTICS_DIR="$HOME/.cortex/diagnostics" +./target/debug/Cortex debug doctor --json +python3 scripts/readiness/insights.py "$CORTEX_DIAGNOSTICS_DIR" +``` + +The application creates private directories/files (0700/0600 on Unix), rejects +symlink/public output directories, and accepts only a closed set of events: +command completion, server startup/requests, and session creation/deletion. +Fields are schema version, time, app version, operation, trace/span IDs, numeric +status, and duration. It never accepts prompts, file paths, headers, tokens, +email addresses, session IDs, response bodies, or arbitrary error text. + +Each process journal is capped at 2 MiB. At startup, only generated +`run-.jsonl` files older than seven days are deleted; other files are untouched. +At most 64 recent files are allowed. On quota/storage errors, diagnostics report +a failure rather than claiming successful recording. Existing app operations +are not turned into successes or failures by a later recording error. +Normal application/session logs are separate and are **not safe diagnostic +attachments**. See [privacy](../reference/privacy.md). + +## Alerts and deployment comparison + +The local insights command exits nonzero for either: + +- at least 20 operations and an error rate of 5% or more; +- at least 20 server requests and p95 response-creation latency above 2,000 ms. + +Empty input is an error, not a healthy deployment. Results group counts, +operations, failures, and latency by the existing application version. Compare +actual before/after deployment samples; low traffic cannot establish an SLO. +No background service or external paging is configured. Operators can invoke +the command from their own local scheduler and route its exit status locally. + +These counters also show local feature use (commands and session lifecycle). +They are not a user-tracking system and cannot establish organization-wide +product adoption. Trace IDs correlate async tasks and incoming HTTP requests +locally. No new trace headers are sent to the remote coding API. Cross-service +distributed tracing remains out of scope under the local-only data policy. + +## Triage a failure + +1. Run `Cortex debug doctor --json`; fix missing Git/ripgrep, malformed TOML, or + failed storage checks. Do not describe these as coding-service outages. +2. For the server, check `/api/v1/health` and authenticated `/metrics`. A 401 is + an authentication problem; 429 means rate limiting. Do not disable auth. +3. Match local request/trace IDs and app version, then inspect aggregate + `insights.py` output. Never copy raw request/session logs into an issue. +4. Reproduce with `python3 scripts/readiness/qa.py` and the narrow relevant + unit/integration test. A real upstream failure must show + **The coding service is temporarily unavailable**, not a fake response. +5. Draft a local issue summary containing version, sanitized reproduction, + expected/actual result, test names, and aggregate counts. Include no secrets, + prompts, customer code, home paths, or authentication details. +6. Only submit the issue after explicit approval. Assign priority and area + according to [maintenance](maintenance.md). Add a regression test and a + runbook update for a genuinely new failure mode. + +## Profiling + +`X-Response-Time`, journal durations, and nextest reports locate slow operations. +For deeper Linux CPU profiling, use the system `perf` tool locally: + +```bash +cargo build --locked --profile profiling -p cortex-cli -p cortex-app-server +perf record --call-graph dwarf -- ./target/profiling/Cortex debug doctor +perf report +``` + +Do not lower system profiling protections or use elevated privileges just to +run this command. If host policy blocks perf, report that limitation. Profiles +can contain process/path information; keep them local and delete them after +investigation. This supplies a profiling procedure and timing instrumentation, +not an always-on CPU profiler. The `profiling` profile preserves debug information +and symbols without changing release artifacts. + +## Release and rollback + +Use the existing version-bump PR workflow on `main`. Keep `VERSION_CLI`, +workspace version, and `src/cortex-cli/VERSION` aligned. Review changelog, CI, +local QA, and versioned diagnostic samples before accepting a release. + +If a deployed CLI regresses, reinstall the last known-good **existing** release +using the documented installer/version mechanism, then rerun local QA and +compare samples. For a managed server, restore the previous tested binary and +restart using the same protected configuration. Do not rewrite tags, delete +sessions, or publish a synthetic release to improve deployment statistics. +Actual release frequency requires genuine shipped changes over time. diff --git a/docs/guides/quality.md b/docs/guides/quality.md new file mode 100644 index 00000000..00886e9c --- /dev/null +++ b/docs/guides/quality.md @@ -0,0 +1,51 @@ +# Source and dependency policy + +```bash +export PATH="$PWD/target/readiness-tools/bin:$PATH" +python -B -m unittest discover -s scripts/readiness -p 'test_*.py' +python scripts/readiness/quality.py --base origin/main +cargo machete --with-metadata +python scripts/readiness/release_age.py --base origin/main +python scripts/readiness/schema.py +``` + +CI supplies the actual PR base SHA (or previous push SHA), not the current +checkout, and fetches history. Never change the baseline to the current commit +to conceal regressions. + +## Enforced controls + +- Lizard 1.17.31 analyzes Rust functions. The complexity target is 25. +- Rust source files target 1,000 lines; all repository files have a 5 MiB cap. +- Duplicate complete function bodies of at least 100 tokens are reported. +- Existing complexity/line-count/duplicate debt is retained in + `target/readiness/quality.json`. New or worsened findings fail. This is a + regression gate, not a claim that the existing tree is debt-free. +- Built-in feature registries are compared to production literal consumers. + Test-only references and comments do not keep a flag alive. Removing the last + consumer or adding an unconsumed flag fails. Existing candidates remain visible. + Dynamic/custom feature consumers need explicit review; lexical analysis cannot + prove that a flag is dead in every external caller. +- New flags need an owner, intended lifetime, rollout/removal decision, and tests + in their PR. Review inactive flags during weekly maintenance; remove abandoned + declarations rather than adding dummy consumers. +- Cargo-machete 0.9.1 checks unused direct dependencies. +- Dependencies already centralized in the workspace must be inherited. + Renamed internal crates must point to the same workspace path. Exact, + justified older-API constraints live in `.quality/dependency-compatibility.json`. + Changed or unused exceptions fail. This preserves compatibility rather than + pretending every major-version migration has been validated. +- Newly locked crates.io versions must be at least seven days old and not yanked. + Unavailable registry evidence fails closed. Existing locked versions are not + re-dated. Git dependencies stay pinned by `Cargo.lock`; review their provenance + separately. Dependabot also has a seven-day Cargo update cooldown. +- Local links and referenced scripts in `AGENTS.md` must exist. +- Generated CLI/API contracts must match their Rust sources. + +For an urgent security upgrade younger than seven days, maintainers must review +a narrow policy change with an advisory reference. Do not bypass the gate, alter +registry dates, or remove `--locked`. + +The existing formatting, Clippy, audit, version, and TUI gates remain required. +CI Success also depends on source policy and changed-line coverage. Test and +coverage artifacts are retained for 14–30 days, not sent to a third-party service. diff --git a/docs/guides/readiness-remediation.md b/docs/guides/readiness-remediation.md new file mode 100644 index 00000000..29e208e8 --- /dev/null +++ b/docs/guides/readiness-remediation.md @@ -0,0 +1,70 @@ +# Readiness remediation snapshot + +Local changes on `chore/agent-readiness`, based on +`2e24cba2ed05d2833edaf06e9e1a34d0f65d6cc5`. This is a validation snapshot, +not a new readiness score or a claim that all historical criteria now pass. +It records validation before publication; subsequent commits and GitHub CI +results belong to the pull request. No release was made. + +## Implemented + +- Source complexity, large-file, duplication, feature-consumer, dependency + inheritance, unused-dependency, and minimum-release-age controls. +- Nextest timing/JUnit reports, independent stability repetitions, and an + enforced 80% floor on changed production lines in CLI/server/common. +- Locked local setup, development container, local doctor, and real-process + CLI/server functional and dynamic security QA. +- Actual server authentication/middleware, JWT/admin checks, request limits, + workspace file boundaries, WebSocket authentication, and safe error handling. +- Opt-in local diagnostics, request correlation, live metrics, local aggregate + alerts, version comparisons, privacy guidance, and a symbol-preserving profile. +- Generated CLI/OpenAPI contracts, documentation checks, repository skills, + ownership, triage guidance, and dependency-update configuration. +- Corrected the CLI plugin-version/verbosity collision, placeholder feature + initialization, stale doctest imports, and an asynchronous append visibility + race exposed by the full test run. No sleeps, retries, or ignored tests were + added to conceal failures. + +The authorized GitHub configuration changes were applied: main requires one +approval, and four priority plus six area labels exist. CODEOWNERS and new CI +controls require the branch to be reviewed and published. + +## Verified locally + +| Check | Result | +| --- | --- | +| `cargo test --locked --offline --workspace --no-fail-fast` | Passed, including doctests and headless TUI tests | +| Nextest workspace, three repetitions | 6,039 passed per run; no observed flaky tests; 19 pre-existing skips per run | +| Local functional/security QA | 14 cases passed against real processes | +| Changed-line production coverage | 750/879 executable lines, 85.32%; no missing production files | +| Python policy tests | 25 passed | +| Source/dependency policy | 18,535 functions analyzed; zero regressions or policy failures | +| Formatting, Clippy, version and whitespace checks | Passed | +| Cargo audit | Passed against 1,239 loaded advisories, without new exceptions | +| Unused dependencies / release-age policy | Passed; two newly locked releases checked | +| Generated contracts | Fresh | +| Development image | Built; unprivileged, network-disabled prerequisite smoke passed | +| Profiling profile | `cargo check --profile profiling -p cortex-common` passed | + +Reproducible commands are in [development](development.md) and +[source quality](quality.md). Detailed local evidence is under ignored +`target/readiness/`. Diagnostic journals and aggregates have no upload step. + +## Remaining limits + +- The scanner retains **178 inherited findings**, including unconsumed-feature + candidates. A no-new-debt gate is not elimination of existing debt or proof + that every dynamic feature consumer is covered. +- Interactive authenticated TUI/coding-service QA requires a dedicated approved + test account and an installed interaction driver. Neither a successful model + turn nor a complete Droid Control QA installation was verified. Local message + storage tests do not stand in for model inference. +- Cross-service tracing, remote alerting, and organization-wide product + analytics were not enabled. Diagnostics remain local-only as requested. +- Release frequency and backlog health need genuine activity over time. + No releases or issues were fabricated to improve these metrics. +- The complete editor-driven Dev Containers post-create flow, CPU sampling + permissions, semantic workflow lint, and non-Linux platforms were not verified. + JSON/YAML parsing and the local container smoke are narrower checks. +- The original persisted readiness report has not been rescored. At the time of + this snapshot, these CI controls had not yet run on GitHub. diff --git a/docs/reference/app-server.md b/docs/reference/app-server.md new file mode 100644 index 00000000..f6ecea24 --- /dev/null +++ b/docs/reference/app-server.md @@ -0,0 +1,79 @@ +# Local app-server API + +This is the standalone `cortex-server`, not the remote Cortex coding API. +Start it from the workspace whose files it may access: + +```bash +cargo build --locked -p cortex-app-server +./target/debug/cortex-server --listen 127.0.0.1:55554 --json-logs --auth +``` + +Supply `CORTEX_SERVER_API_KEY` from a local secret manager or protected process +environment. `CORTEX_JWT_SECRET` enables JWT verification instead. For compatibility, +`CORTEX_API_KEY` is still accepted by environment-only server configuration; +prefer the distinct server key so coding-service and local-server credentials +are not reused. Never put values in checked-in JSON, examples, shell history, +or command arguments. + +`--config FILE` reads a JSON `ServerConfig`. Environment server credentials are +applied to both file and environment configuration. `--listen` explicitly +overrides either. `--auth` requires configured credentials. +The CLI's `Cortex serve` command also accepts `CORTEX_SERVER_API_KEY` and +`CORTEX_JWT_SECRET`; prefer these to its legacy command-line token option. + +The default listener is loopback. A non-loopback listener without authentication +fails before binding. Use a trusted TLS reverse proxy for remote access; direct +TLS is not implemented and a nonempty TLS configuration fails explicitly. +mDNS is opt-in. Empty `cors_origins` denies cross-origin browser access; list +the exact trusted browser origins in configuration. + +## Contract + +[OpenAPI 3.1 JSON](app-server.openapi.json) is generated from the handler models +with schemars 0.8.22 and served at authenticated `GET /api/v1/openapi.json`. +The supported contract covers: + +| Method | Path (under `/api/v1`) | Meaning | +| --- | --- | --- | +| GET | `/health` | Anonymous local readiness, **not** remote coding-service health | +| GET | `/metrics` | Authenticated request/error/session counters | +| POST, GET | `/sessions` | Create/list in-memory sessions | +| GET, DELETE | `/sessions/{id}` | Read/delete an in-memory session | +| POST, GET | `/sessions/{id}/messages` | Store/list messages, no model inference | + +Other development endpoints, including files, terminals, admin, SSE, and +WebSockets, are not yet part of this stable schema. Authentication applies to +them too. A configured server API key is an operator credential, not a +multi-tenant sandbox. JWT admin routes require the `admin` role; ordinary +authenticated routes operate on the server's workspace. Do not host mutually +untrusted tenants in one process. + +Send `Authorization: ApiKey ` or `Authorization: Bearer `. +JWTs require issuer `Cortex` and audience `cortex-api`. +WebSockets require authentication at the HTTP upgrade; query-string tokens do +not grant access. Client `Auth` messages verify JWTs rather than accepting any +string. Anonymous endpoint exceptions match exact paths, never prefixes. + +Every response has `X-Request-Id`, `traceparent`, and `X-Response-Time`. +Only UUID request IDs and valid W3C trace contexts are retained from callers. +These support local correlation. No exporter sends diagnostic data elsewhere. +The timing covers response creation/headers, not the lifetime of an SSE stream. + +Rate limiting uses the connected peer address unless explicitly configured to +trust proxy headers. Configure that only behind a trusted proxy. Limits also +apply to bodies without `Content-Length`; a limit failure returns 413. +API file operations are constrained to the opened workspace, not every home, +temporary directory, or mounted drive. + +## Maintain generated contracts + +```bash +python3 scripts/readiness/schema.py --write +python3 scripts/readiness/schema.py +``` + +The [CLI command inventory](cli.commands.json) is generated from Clap 4.6.6 on Linux. +The remote coding API contract is maintained by its service; the client-side +contract and tested status mappings are in +[`code_agent.rs`](../../src/cortex-engine/src/client/code_agent.rs). +Do not invent undocumented remote endpoints. diff --git a/docs/reference/app-server.openapi.json b/docs/reference/app-server.openapi.json new file mode 100644 index 00000000..5b8a2ee9 --- /dev/null +++ b/docs/reference/app-server.openapi.json @@ -0,0 +1,640 @@ +{ + "components": { + "schemas": { + "CreateSessionRequest": { + "description": "Create session request.", + "properties": { + "metadata": { + "default": null + }, + "model": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "system_prompt": { + "default": null, + "type": [ + "string", + "null" + ] + } + }, + "title": "CreateSessionRequest", + "type": "object" + }, + "HealthResponse": { + "description": "Health check response.", + "properties": { + "status": { + "type": "string" + }, + "uptime_seconds": { + "format": "uint64", + "minimum": 0.0, + "type": "integer" + }, + "version": { + "type": "string" + } + }, + "required": [ + "status", + "uptime_seconds", + "version" + ], + "title": "HealthResponse", + "type": "object" + }, + "MessageResponse": { + "description": "Message response.", + "properties": { + "content": { + "type": "string" + }, + "id": { + "type": "string" + }, + "role": { + "type": "string" + }, + "tokens": { + "format": "uint32", + "minimum": 0.0, + "type": "integer" + }, + "tool_calls": { + "items": { + "$ref": "#/components/schemas/ToolCallResponse" + }, + "type": [ + "array", + "null" + ] + } + }, + "required": [ + "content", + "id", + "role", + "tokens" + ], + "title": "MessageResponse", + "type": "object" + }, + "MetricsSnapshot": { + "description": "Metrics snapshot.", + "properties": { + "active_sessions": { + "description": "Active sessions.", + "format": "uint", + "minimum": 0.0, + "type": "integer" + }, + "errors": { + "description": "Errors.", + "format": "uint64", + "minimum": 0.0, + "type": "integer" + }, + "rate_limit_hits": { + "description": "Rate limit hits.", + "format": "uint64", + "minimum": 0.0, + "type": "integer" + }, + "sessions_created": { + "description": "Sessions created.", + "format": "uint64", + "minimum": 0.0, + "type": "integer" + }, + "total_requests": { + "description": "Total requests.", + "format": "uint64", + "minimum": 0.0, + "type": "integer" + }, + "uptime_seconds": { + "description": "Server uptime in seconds.", + "format": "uint64", + "minimum": 0.0, + "type": "integer" + } + }, + "required": [ + "active_sessions", + "errors", + "rate_limit_hits", + "sessions_created", + "total_requests", + "uptime_seconds" + ], + "title": "MetricsSnapshot", + "type": "object" + }, + "SendMessageRequest": { + "description": "Send message request.", + "properties": { + "content": { + "type": "string" + }, + "role": { + "default": null, + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "content" + ], + "title": "SendMessageRequest", + "type": "object" + }, + "SessionListItem": { + "description": "Session list item.", + "properties": { + "id": { + "type": "string" + }, + "message_count": { + "format": "uint", + "minimum": 0.0, + "type": "integer" + }, + "model": { + "type": "string" + }, + "status": { + "type": "string" + }, + "total_tokens": { + "format": "uint64", + "minimum": 0.0, + "type": "integer" + } + }, + "required": [ + "id", + "message_count", + "model", + "status", + "total_tokens" + ], + "title": "SessionListItem", + "type": "object" + }, + "SessionResponse": { + "description": "Session response.", + "properties": { + "id": { + "type": "string" + }, + "message_count": { + "format": "uint", + "minimum": 0.0, + "type": "integer" + }, + "metadata": true, + "model": { + "type": "string" + }, + "status": { + "type": "string" + }, + "system_prompt": { + "type": [ + "string", + "null" + ] + }, + "total_tokens": { + "format": "uint64", + "minimum": 0.0, + "type": "integer" + } + }, + "required": [ + "id", + "message_count", + "model", + "status", + "total_tokens" + ], + "title": "SessionResponse", + "type": "object" + }, + "ToolCallResponse": { + "description": "Tool call response.", + "properties": { + "arguments": true, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "result": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "arguments", + "id", + "name" + ], + "type": "object" + } + }, + "securitySchemes": { + "bearerAuth": { + "bearerFormat": "JWT", + "scheme": "bearer", + "type": "http" + }, + "serverApiKey": { + "description": "Value: ApiKey followed by the server key", + "in": "header", + "name": "Authorization", + "type": "apiKey" + } + } + }, + "info": { + "description": "Supported local session/health contract. Message POST stores a message; it does not generate a model response. Other development endpoints are not part of this stable contract.", + "title": "Cortex local session API", + "version": "0.1.7" + }, + "openapi": "3.1.0", + "paths": { + "/health": { + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HealthResponse" + } + } + }, + "description": "Success" + }, + "400": { + "description": "Invalid JSON or request" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Resource not found or endpoint disabled" + }, + "413": { + "description": "Request body too large" + }, + "429": { + "description": "Rate limited; Retry-After header contains seconds" + }, + "503": { + "description": "Local server is not ready" + } + }, + "security": [], + "summary": "Local readiness, not coding-service availability" + } + }, + "/metrics": { + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetricsSnapshot" + } + } + }, + "description": "Success" + }, + "400": { + "description": "Invalid JSON or request" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Resource not found or endpoint disabled" + }, + "413": { + "description": "Request body too large" + }, + "429": { + "description": "Rate limited; Retry-After header contains seconds" + }, + "503": { + "description": "Local server is not ready" + } + }, + "summary": "Local request counters" + } + }, + "/sessions": { + "get": { + "parameters": [ + { + "in": "query", + "name": "limit", + "schema": { + "default": 20, + "minimum": 0, + "type": "integer" + } + }, + { + "in": "query", + "name": "offset", + "schema": { + "default": 0, + "minimum": 0, + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/SessionListItem" + }, + "type": "array" + } + } + }, + "description": "Success" + }, + "400": { + "description": "Invalid JSON or request" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Resource not found or endpoint disabled" + }, + "413": { + "description": "Request body too large" + }, + "429": { + "description": "Rate limited; Retry-After header contains seconds" + }, + "503": { + "description": "Local server is not ready" + } + }, + "summary": "List in-memory sessions" + }, + "post": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateSessionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionResponse" + } + } + }, + "description": "Success" + }, + "400": { + "description": "Invalid JSON or request" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Resource not found or endpoint disabled" + }, + "413": { + "description": "Request body too large" + }, + "429": { + "description": "Rate limited; Retry-After header contains seconds" + }, + "503": { + "description": "Local server is not ready" + } + }, + "summary": "Create an in-memory session" + } + }, + "/sessions/{id}": { + "delete": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "deleted": { + "const": true + } + }, + "required": [ + "deleted" + ], + "type": "object" + } + } + }, + "description": "Success" + }, + "400": { + "description": "Invalid JSON or request" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Resource not found or endpoint disabled" + }, + "413": { + "description": "Request body too large" + }, + "429": { + "description": "Rate limited; Retry-After header contains seconds" + }, + "503": { + "description": "Local server is not ready" + } + }, + "summary": "Delete a session" + }, + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionResponse" + } + } + }, + "description": "Success" + }, + "400": { + "description": "Invalid JSON or request" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Resource not found or endpoint disabled" + }, + "413": { + "description": "Request body too large" + }, + "429": { + "description": "Rate limited; Retry-After header contains seconds" + }, + "503": { + "description": "Local server is not ready" + } + }, + "summary": "Get a session" + }, + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/sessions/{id}/messages": { + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/MessageResponse" + }, + "type": "array" + } + } + }, + "description": "Success" + }, + "400": { + "description": "Invalid JSON or request" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Resource not found or endpoint disabled" + }, + "413": { + "description": "Request body too large" + }, + "429": { + "description": "Rate limited; Retry-After header contains seconds" + }, + "503": { + "description": "Local server is not ready" + } + }, + "summary": "List stored messages" + }, + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "post": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SendMessageRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + } + }, + "description": "Success" + }, + "400": { + "description": "Invalid JSON or request" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Resource not found or endpoint disabled" + }, + "413": { + "description": "Request body too large" + }, + "429": { + "description": "Rate limited; Retry-After header contains seconds" + }, + "503": { + "description": "Local server is not ready" + } + }, + "summary": "Store a message without invoking a model" + } + } + }, + "security": [ + { + "serverApiKey": [] + }, + { + "bearerAuth": [] + } + ], + "servers": [ + { + "url": "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/api/v1" + } + ] +} diff --git a/docs/reference/cli.commands.json b/docs/reference/cli.commands.json new file mode 100644 index 00000000..84b26d08 --- /dev/null +++ b/docs/reference/cli.commands.json @@ -0,0 +1,9835 @@ +{ + "about": "Cortex - AI Coding Agent", + "arguments": [ + { + "help": "Configuration overrides in the form key=value", + "id": "raw_overrides", + "long": "config", + "required": false, + "short": "c" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Model to use (e.g., claude-sonnet-4-20250514, gpt-4o, gemini-2.0-flash)", + "id": "model", + "long": "model", + "required": false, + "short": "m" + }, + { + "help": "Use open-source/local LLM providers instead of cloud APIs", + "id": "oss", + "long": "oss", + "required": false, + "short": null + }, + { + "help": "Configuration profile from config.toml", + "id": "config_profile", + "long": "profile", + "required": false, + "short": "p" + }, + { + "help": "Select the sandbox policy for shell commands", + "id": "sandbox_mode", + "long": "sandbox", + "required": false, + "short": "s" + }, + { + "help": "Set the approval policy for tool executions", + "id": "approval_policy", + "long": "ask-for-approval", + "required": false, + "short": "a" + }, + { + "help": "Enable fully automatic mode with sandboxed execution", + "id": "full_auto", + "long": "full-auto", + "required": false, + "short": null + }, + { + "help": "Skip all confirmation prompts and execute commands without sandboxing. DANGEROUS!", + "id": "dangerously_bypass_approvals_and_sandbox", + "long": "dangerously-bypass-approvals-and-sandbox", + "required": false, + "short": null + }, + { + "help": "Tell the agent to use the specified directory as its working root", + "id": "cwd", + "long": "cd", + "required": false, + "short": "C" + }, + { + "help": "Additional directories that should be writable", + "id": "add_dir", + "long": "add-dir", + "required": false, + "short": null + }, + { + "help": "Image files to attach to the initial prompt", + "id": "images", + "long": "image", + "required": false, + "short": "i" + }, + { + "help": "Enable web search capability for the agent", + "id": "web_search", + "long": "search", + "required": false, + "short": null + }, + { + "help": "Enter the alternate screen buffer. Default is **always** (full viewport). Same as `[tui] alternate_screen = true`. Use `--no-alternate-screen` to stay inline", + "id": "alternate_screen", + "long": "alternate-screen", + "required": false, + "short": null + }, + { + "help": "Stay inline in the host terminal (never alternate screen). Same as `[tui] alternate_screen = false`", + "id": "no_alternate_screen", + "long": "no-alternate-screen", + "required": false, + "short": null + }, + { + "help": "Maximum number of concurrent agent threads", + "id": "max_agent_threads", + "long": "max-agent-threads", + "required": false, + "short": null + }, + { + "help": "Maximum number of concurrent tool executions", + "id": "max_tool_threads", + "long": "max-tool-threads", + "required": false, + "short": null + }, + { + "help": "Timeout for shell commands in seconds", + "id": "command_timeout", + "long": "command-timeout", + "required": false, + "short": null + }, + { + "help": "Timeout for HTTP requests in seconds", + "id": "http_timeout", + "long": "http-timeout", + "required": false, + "short": null + }, + { + "help": "Disable streaming responses", + "id": "no_streaming", + "long": "no-streaming", + "required": false, + "short": null + }, + { + "help": "Set log verbosity level (error, warn, info, debug, trace)", + "id": "log_level", + "long": "log-level", + "required": false, + "short": "L" + }, + { + "help": "Enable debug mode: writes ALL trace-level logs to ./debug.txt", + "id": "debug", + "long": "debug", + "required": false, + "short": null + }, + { + "help": "Initial prompt (if no subcommand)", + "id": "prompt", + "long": null, + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + }, + { + "help": "Print version", + "id": "version", + "long": "version", + "required": false, + "short": "V" + } + ], + "name": "cortex", + "subcommands": [ + { + "about": "Run Cortex non-interactively with advanced options", + "arguments": [ + { + "help": "Message to send to the AI agent. Multiple arguments are joined with spaces. Note: Prompts starting with a dash (e.g., \"--help explain this flag\") are supported and won't be interpreted as flags", + "id": "message", + "long": null, + "required": false, + "short": null + }, + { + "help": "Execute a predefined command instead of a prompt. Use message arguments as command arguments", + "id": "command", + "long": "command", + "required": false, + "short": null + }, + { + "help": "Continue the most recent session", + "id": "continue_session", + "long": "continue", + "required": false, + "short": "c" + }, + { + "help": "Specify a session ID to continue", + "id": "session_id", + "long": "session", + "required": false, + "short": "s" + }, + { + "help": "Automatically share the session and print the share URL", + "id": "share", + "long": "share", + "required": false, + "short": null + }, + { + "help": "Model to use in provider/model format (e.g., anthropic/claude-3-5-sonnet)", + "id": "model", + "long": "model", + "required": false, + "short": "m" + }, + { + "help": "Agent to use for this request", + "id": "agent", + "long": "agent", + "required": false, + "short": null + }, + { + "help": "Output format: default (formatted), json, or jsonl", + "id": "format", + "long": "format", + "required": false, + "short": null + }, + { + "help": "Output format alias (same as --format). Valid values: default, json, jsonl", + "id": "output", + "long": "output", + "required": false, + "short": null + }, + { + "help": "File(s) to attach to the message. Can be specified multiple times", + "id": "files", + "long": "file", + "required": false, + "short": "f" + }, + { + "help": "Title for the session. If empty string is provided, uses truncated prompt", + "id": "title", + "long": "title", + "required": false, + "short": null + }, + { + "help": "Attach to a running Cortex server instead of starting locally. Value should be the server URL (e.g., \"http://localhost:3000\"). Use this to connect to a remote Cortex instance or an existing local server started with 'cortex serve'", + "id": "attach", + "long": "attach", + "required": false, + "short": null + }, + { + "help": "Port for the local server (defaults to random port)", + "id": "port", + "long": "port", + "required": false, + "short": null + }, + { + "help": "Model temperature (0.0-2.0). Lower values make output more deterministic", + "id": "temperature", + "long": "temperature", + "required": false, + "short": "t" + }, + { + "help": "Top-p (nucleus) sampling parameter. Controls diversity of token selection", + "id": "top_p", + "long": "top-p", + "required": false, + "short": null + }, + { + "help": "Top-k sampling parameter. Limits token selection to k most probable tokens. Note: Not all providers support combining --top-k with --temperature", + "id": "top_k", + "long": "top-k", + "required": false, + "short": null + }, + { + "help": "Random seed for reproducible outputs", + "id": "seed", + "long": "seed", + "required": false, + "short": null + }, + { + "help": "Send a desktop notification when the task completes", + "id": "notification", + "long": "notification", + "required": false, + "short": "n" + }, + { + "help": "Stream output as it arrives (default behavior). Use --no-stream to buffer and wait for the complete response", + "id": "stream", + "long": "stream", + "required": false, + "short": null + }, + { + "help": "Disable streaming - wait for complete response before outputting. This is the opposite of --stream (which is enabled by default)", + "id": "no_stream", + "long": "no-stream", + "required": false, + "short": null + }, + { + "help": "Copy the final AI response to the system clipboard", + "id": "copy", + "long": "copy", + "required": false, + "short": "C" + }, + { + "help": "Save the final response to a file. Parent directory will be created automatically if it doesn't exist", + "id": "output_file", + "long": "output-file", + "required": false, + "short": "o" + }, + { + "help": "Working directory override", + "id": "cwd", + "long": "cwd", + "required": false, + "short": null + }, + { + "help": "Additional directories that should be writable. Can be specified multiple times to add multiple directories", + "id": "add_dir", + "long": "add-dir", + "required": false, + "short": null + }, + { + "help": "Enable verbose/debug output", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Timeout in seconds (0 for no timeout)", + "id": "timeout", + "long": "timeout", + "required": false, + "short": null + }, + { + "help": "Preview what would be sent without executing. Shows estimated token counts including system prompt and tool definitions", + "id": "dry_run", + "long": "dry-run", + "required": false, + "short": null + }, + { + "help": "Maximum tokens for response (used for token validation). If specified, cortex will validate that prompt + max_tokens does not exceed the model's context limit before making the API call", + "id": "max_tokens", + "long": "max-tokens", + "required": false, + "short": null + }, + { + "help": "Custom system prompt to use instead of the default. Defines the AI's persona and behavior", + "id": "system_prompt", + "long": "system", + "required": false, + "short": null + }, + { + "help": "Path to a JSON schema file for structured output. When provided, the AI response will be validated against this schema", + "id": "schema", + "long": "schema", + "required": false, + "short": null + }, + { + "help": "Suppress non-essential output (quiet mode). Only show the final result or errors", + "id": "quiet", + "long": "quiet", + "required": false, + "short": "q" + }, + { + "help": "Disable progress indicators (useful for CI/CD environments)", + "id": "no_progress", + "long": "no-progress", + "required": false, + "short": null + }, + { + "help": "Bypass any cached responses and force a fresh request", + "id": "no_cache", + "long": "no-cache", + "required": false, + "short": null + }, + { + "help": "Number of times to retry failed requests (default: 0)", + "id": "retry", + "long": "retry", + "required": false, + "short": null + }, + { + "help": "Frequency penalty (-2.0 to 2.0). Positive values penalize tokens based on their frequency in the text so far, decreasing the likelihood of repeating the same content verbatim", + "id": "frequency_penalty", + "long": "frequency-penalty", + "required": false, + "short": null + }, + { + "help": "Presence penalty (-2.0 to 2.0). Positive values penalize new tokens based on whether they appear in the text so far, increasing the likelihood of talking about new topics", + "id": "presence_penalty", + "long": "presence-penalty", + "required": false, + "short": null + }, + { + "help": "Stop sequences (can be specified multiple times). Generation will stop when any of these sequences is encountered", + "id": "stop_sequences", + "long": "stop", + "required": false, + "short": null + }, + { + "help": "Request log probabilities for output tokens. Returns the log probabilities of the most likely tokens (up to 5)", + "id": "logprobs", + "long": "logprobs", + "required": false, + "short": null + }, + { + "help": "Number of completions to generate. Returns multiple independent completions for the same prompt", + "id": "num_completions", + "long": "n", + "required": false, + "short": null + }, + { + "help": "Generate best_of completions and return the best one. Must be greater than n if n is specified", + "id": "best_of", + "long": "best-of", + "required": false, + "short": null + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "run", + "subcommands": [] + }, + { + "about": "Execute in headless mode (for CI/CD, scripts, automation)", + "arguments": [ + { + "help": "The prompt to execute. Can also be provided via stdin or --file. Prompts starting with a dash (e.g., \"--help explain this\") are supported", + "id": "prompt", + "long": null, + "required": false, + "short": null + }, + { + "help": "Read prompt from file", + "id": "file", + "long": "file", + "required": false, + "short": "f" + }, + { + "help": "Output format", + "id": "output_format", + "long": "output-format", + "required": false, + "short": "o" + }, + { + "help": "Input format (for multi-turn sessions)", + "id": "input_format", + "long": "input-format", + "required": false, + "short": null + }, + { + "help": "Autonomy level for operations. - read-only: No modifications (default, safest) - low: Basic file operations only - medium: Package install, builds, local git - high: Full access including git push", + "id": "autonomy", + "long": "auto", + "required": false, + "short": null + }, + { + "help": "Skip ALL permission checks (DANGEROUS). Only use in completely isolated environments like Docker containers. Cannot be combined with --auto", + "id": "skip_permissions", + "long": "skip-permissions-unsafe", + "required": false, + "short": null + }, + { + "help": "Model ID to use", + "id": "model", + "long": "model", + "required": false, + "short": "m" + }, + { + "help": "Model ID to use for spec mode", + "id": "spec_model", + "long": "spec-model", + "required": false, + "short": null + }, + { + "help": "Start in specification mode (plan before executing)", + "id": "use_spec", + "long": "use-spec", + "required": false, + "short": null + }, + { + "help": "Reasoning effort level", + "id": "reasoning_effort", + "long": "reasoning-effort", + "required": false, + "short": "r" + }, + { + "help": "Session ID to continue (requires a prompt)", + "id": "session_id", + "long": "session-id", + "required": false, + "short": "s" + }, + { + "help": "Enable specific tools (comma or space separated)", + "id": "enabled_tools", + "long": "enabled-tools", + "required": false, + "short": null + }, + { + "help": "Disable specific tools (comma or space separated)", + "id": "disabled_tools", + "long": "disabled-tools", + "required": false, + "short": null + }, + { + "help": "List available tools for the selected model and exit", + "id": "list_tools", + "long": "list-tools", + "required": false, + "short": null + }, + { + "help": "Working directory path", + "id": "cwd", + "long": "cwd", + "required": false, + "short": null + }, + { + "help": "Maximum number of turns before stopping", + "id": "max_turns", + "long": "max-turns", + "required": false, + "short": null + }, + { + "help": "Timeout in seconds (0 for no timeout)", + "id": "timeout", + "long": "timeout", + "required": false, + "short": null + }, + { + "help": "Image files to attach to the prompt", + "id": "images", + "long": "image", + "required": false, + "short": "i" + }, + { + "help": "Verbose output (show tool calls, reasoning)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Custom system prompt to use instead of the default. Defines the AI's persona and behavior for this execution", + "id": "system_prompt", + "long": "system", + "required": false, + "short": null + }, + { + "help": "Maximum tokens for response generation. Limits the length of the AI's response", + "id": "max_tokens", + "long": "max-tokens", + "required": false, + "short": null + }, + { + "help": "Include the prompt in the output (echo mode). When enabled, the original prompt will be included at the beginning of the response", + "id": "echo", + "long": "echo", + "required": false, + "short": null + }, + { + "help": "User identifier for tracking and rate limiting. This ID is passed to the API for usage tracking purposes", + "id": "user", + "long": "user", + "required": false, + "short": null + }, + { + "help": "Suffix text for completion insertion mode. The model will generate text to insert between the prompt and this suffix", + "id": "suffix", + "long": "suffix", + "required": false, + "short": null + }, + { + "help": "Response format for structured output. Valid values: text, json, json_object", + "id": "response_format", + "long": "response-format", + "required": false, + "short": null + }, + { + "help": "URLs to fetch and include in the context. Content from these URLs will be fetched and added to the prompt", + "id": "urls", + "long": "url", + "required": false, + "short": null + }, + { + "help": "Read input from the system clipboard. The clipboard content will be appended to the prompt", + "id": "clipboard", + "long": "clipboard", + "required": false, + "short": null + }, + { + "help": "Include current git diff in the context. Useful for code review tasks", + "id": "git_diff", + "long": "git-diff", + "required": false, + "short": null + }, + { + "help": "Include only files matching these patterns. Supports glob patterns like \"*.py\" or \"src/**/*.rs\"", + "id": "include_patterns", + "long": "include", + "required": false, + "short": null + }, + { + "help": "Exclude files matching these patterns. Supports glob patterns like \"*.test.js\" or \"node_modules/**\"", + "id": "exclude_patterns", + "long": "exclude", + "required": false, + "short": null + }, + { + "help": "Frequency penalty (-2.0 to 2.0). Positive values penalize tokens based on their frequency in the text so far, decreasing the likelihood of repeating the same content verbatim", + "id": "frequency_penalty", + "long": "frequency-penalty", + "required": false, + "short": null + }, + { + "help": "Presence penalty (-2.0 to 2.0). Positive values penalize new tokens based on whether they appear in the text so far, increasing the likelihood of talking about new topics", + "id": "presence_penalty", + "long": "presence-penalty", + "required": false, + "short": null + }, + { + "help": "Stop sequences (can be specified multiple times). Generation will stop when any of these sequences is encountered", + "id": "stop_sequences", + "long": "stop", + "required": false, + "short": null + }, + { + "help": "Request log probabilities for output tokens. Returns the log probabilities of the most likely tokens (up to 5)", + "id": "logprobs", + "long": "logprobs", + "required": false, + "short": null + }, + { + "help": "Number of completions to generate. Returns multiple independent completions for the same prompt", + "id": "num_completions", + "long": "n", + "required": false, + "short": "n" + }, + { + "help": "Generate best_of completions and return the best one. Must be greater than n if n is specified", + "id": "best_of", + "long": "best-of", + "required": false, + "short": null + }, + { + "help": "JSON schema for structured output. Can be inline JSON (e.g., '{\"type\":\"object\",\"properties\":{...}}') or a path to a JSON schema file", + "id": "output_schema", + "long": "output-schema", + "required": false, + "short": null + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "exec", + "subcommands": [] + }, + { + "about": "Resume a previous interactive session", + "arguments": [ + { + "help": "Session ID to resume (or \"last\" for most recent)", + "id": "session_id", + "long": null, + "required": false, + "short": null + }, + { + "help": "Continue the most recent session without showing the picker", + "id": "last", + "long": "last", + "required": false, + "short": null + }, + { + "help": "Show interactive picker to select from recent sessions", + "id": "pick", + "long": "pick", + "required": false, + "short": null + }, + { + "help": "Show all sessions (disables cwd filtering)", + "id": "all", + "long": "all", + "required": false, + "short": null + }, + { + "help": "Do not persist session changes (incompatible with resume, will error)", + "id": "no_session", + "long": "no-session", + "required": false, + "short": null + }, + { + "help": "Configuration overrides in the form key=value", + "id": "raw_overrides", + "long": "config", + "required": false, + "short": "c" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "resume", + "subcommands": [] + }, + { + "about": "List previous sessions", + "arguments": [ + { + "help": "Show all sessions including from other directories", + "id": "all", + "long": "all", + "required": false, + "short": null + }, + { + "help": "Show sessions from the last N days", + "id": "days", + "long": "days", + "required": false, + "short": null + }, + { + "help": "Show sessions since this date (YYYY-MM-DD)", + "id": "since", + "long": "since", + "required": false, + "short": null + }, + { + "help": "Show sessions until this date (YYYY-MM-DD)", + "id": "until", + "long": "until", + "required": false, + "short": null + }, + { + "help": "Show only favorite sessions", + "id": "favorites", + "long": "favorites", + "required": false, + "short": null + }, + { + "help": "Search sessions by title or ID", + "id": "search", + "long": "search", + "required": false, + "short": "s" + }, + { + "help": "Maximum number of sessions to show", + "id": "limit", + "long": "limit", + "required": false, + "short": "l" + }, + { + "help": "Output in JSON format", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "sessions", + "subcommands": [] + }, + { + "about": "Export a session to JSON format", + "arguments": [ + { + "help": "Session ID to export (interactive picker if not provided)", + "id": "session_id", + "long": null, + "required": false, + "short": null + }, + { + "help": "Output file path (stdout if not specified)", + "id": "output", + "long": "output", + "required": false, + "short": "o" + }, + { + "help": "Output format (json, yaml, csv)", + "id": "format", + "long": "format", + "required": false, + "short": "f" + }, + { + "help": "Pretty-print the output (for json/yaml)", + "id": "pretty", + "long": "pretty", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "export", + "subcommands": [] + }, + { + "about": "Import a session from JSON file or URL", + "arguments": [ + { + "help": "Path to the JSON file to import, URL to fetch, or \"-\" for stdin", + "id": "source", + "long": null, + "required": true, + "short": null + }, + { + "help": "Force import even if session already exists", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Resume the imported session after import", + "id": "resume", + "long": "resume", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "import", + "subcommands": [] + }, + { + "about": "Delete a session", + "arguments": [ + { + "help": "Session ID to delete (full UUID or 8-character prefix)", + "id": "session_id", + "long": null, + "required": true, + "short": null + }, + { + "help": "Skip confirmation prompt", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Force deletion even if session is locked", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "delete", + "subcommands": [] + }, + { + "about": "Authenticate with Cortex API", + "arguments": [ + { + "help": "Read the API key from stdin", + "id": "with_api_key", + "long": "with-api-key", + "required": false, + "short": null + }, + { + "help": "Provide API token directly (for CI/CD automation)", + "id": "token", + "long": "token", + "required": false, + "short": null + }, + { + "help": "Use device code authentication flow", + "id": "use_device_code", + "long": "device-auth", + "required": false, + "short": null + }, + { + "help": "Use enterprise SSO authentication", + "id": "use_sso", + "long": "sso", + "required": false, + "short": null + }, + { + "help": "Override the OAuth issuer base URL (advanced)", + "id": "issuer_base_url", + "long": "experimental_issuer", + "required": false, + "short": null + }, + { + "help": "Override the OAuth client ID (advanced)", + "id": "client_id", + "long": "experimental_client-id", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "login", + "subcommands": [ + { + "about": "Show login status", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "status", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Show login status", + "arguments": [], + "name": "status", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Remove stored authentication credentials", + "arguments": [ + { + "help": "Skip confirmation prompt and log out immediately", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Log out from all logged in accounts", + "id": "all", + "long": "all", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "logout", + "subcommands": [] + }, + { + "about": "Show currently authenticated user", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "whoami", + "subcommands": [] + }, + { + "about": "Manage agents (list, create, show)", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "agent", + "subcommands": [ + { + "about": "List all available agents", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Show only primary agents", + "id": "primary", + "long": "primary", + "required": false, + "short": null + }, + { + "help": "Show only subagents", + "id": "subagents", + "long": "subagents", + "required": false, + "short": null + }, + { + "help": "Show all agents including hidden ones", + "id": "all", + "long": "all", + "required": false, + "short": null + }, + { + "help": "List agents from remote registry", + "id": "remote", + "long": "remote", + "required": false, + "short": null + }, + { + "help": "Filter agents by pattern (glob-style matching). Example: --filter \"python*\" or --filter \"*test*\"", + "id": "filter", + "long": "filter", + "required": false, + "short": null + }, + { + "help": "Output only agent names (one per line) for shell completion", + "id": "names_only", + "long": "names-only", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Show details for a specific agent", + "arguments": [ + { + "help": "Name of the agent to display", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Show how agent would work with a specific model override", + "id": "model", + "long": "model", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "show", + "subcommands": [] + }, + { + "about": "Create a new agent interactively", + "arguments": [ + { + "help": "Agent name (if not provided, interactive mode will prompt)", + "id": "name", + "long": "name", + "required": false, + "short": "n" + }, + { + "help": "Agent description", + "id": "description", + "long": "description", + "required": false, + "short": "d" + }, + { + "help": "Agent mode: primary, subagent, or all", + "id": "mode", + "long": "mode", + "required": false, + "short": "m" + }, + { + "help": "Skip interactive prompts and use defaults", + "id": "non_interactive", + "long": "non-interactive", + "required": false, + "short": null + }, + { + "help": "Generate agent using AI from a natural language description. Example: --generate \"A Rust expert that helps with memory safety and performance\" Note: This feature requires authentication. Run 'cortex login' first or set CORTEX_AUTH_TOKEN", + "id": "generate", + "long": "generate", + "required": false, + "short": "g" + }, + { + "help": "Model to use for AI generation (default: gpt-4o)", + "id": "model", + "long": "model", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "create", + "subcommands": [] + }, + { + "about": "Edit an existing agent in your default editor", + "arguments": [ + { + "help": "Name of the agent to edit", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Editor to use (defaults to $EDITOR or $VISUAL)", + "id": "editor", + "long": "editor", + "required": false, + "short": "e" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "edit", + "subcommands": [] + }, + { + "about": "Remove a user-defined agent", + "arguments": [ + { + "help": "Name of the agent to remove", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Force removal without confirmation", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "remove", + "subcommands": [] + }, + { + "about": "Install an agent from the registry", + "arguments": [ + { + "help": "Name or URL of the agent to install from registry", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Force overwrite if agent already exists", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Registry URL to install from (defaults to official registry)", + "id": "registry", + "long": "registry", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "install", + "subcommands": [] + }, + { + "about": "Copy/clone an existing agent with a new name", + "arguments": [ + { + "help": "Name of the agent to copy", + "id": "source", + "long": null, + "required": true, + "short": null + }, + { + "help": "Name for the new agent copy", + "id": "destination", + "long": null, + "required": true, + "short": null + }, + { + "help": "Force overwrite if destination agent already exists", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "copy", + "subcommands": [] + }, + { + "about": "Export an agent definition to stdout or a file", + "arguments": [ + { + "help": "Name of the agent to export", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output file path (defaults to stdout)", + "id": "output", + "long": "output", + "required": false, + "short": "o" + }, + { + "help": "Export as JSON instead of markdown", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "export", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "List all available agents", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Show details for a specific agent", + "arguments": [], + "name": "show", + "subcommands": [] + }, + { + "about": "Create a new agent interactively", + "arguments": [], + "name": "create", + "subcommands": [] + }, + { + "about": "Edit an existing agent in your default editor", + "arguments": [], + "name": "edit", + "subcommands": [] + }, + { + "about": "Remove a user-defined agent", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "Install an agent from the registry", + "arguments": [], + "name": "install", + "subcommands": [] + }, + { + "about": "Copy/clone an existing agent with a new name", + "arguments": [], + "name": "copy", + "subcommands": [] + }, + { + "about": "Export an agent definition to stdout or a file", + "arguments": [], + "name": "export", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Manage MCP (Model Context Protocol) servers", + "arguments": [ + { + "help": "Configuration overrides in the form key=value", + "id": "raw_overrides", + "long": "config", + "required": false, + "short": "c" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "mcp", + "subcommands": [ + { + "about": "List configured MCP servers", + "arguments": [ + { + "help": "Output the configured servers as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Show all servers including disabled ones", + "id": "all", + "long": "all", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Show details for a configured MCP server", + "arguments": [ + { + "help": "Name of the MCP server to display", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output the server configuration as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "get", + "subcommands": [] + }, + { + "about": "Add a global MCP server entry", + "arguments": [ + { + "help": "Name for the MCP server configuration", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Overwrite existing server configuration if it exists", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Allow localhost and private network URLs (for local development). By default, URLs containing localhost, 127.0.0.1, or private network addresses are blocked for security. Use this flag to enable local development servers", + "id": "allow_local", + "long": "allow-local", + "required": false, + "short": null + }, + { + "help": "Command to launch the MCP server", + "id": "command", + "long": null, + "required": false, + "short": null + }, + { + "help": "Environment variables to set when launching the server", + "id": "env", + "long": "env", + "required": false, + "short": null + }, + { + "help": "URL for a streamable HTTP MCP server", + "id": "url", + "long": "url", + "required": true, + "short": null + }, + { + "help": "Name of the environment variable containing a bearer token (not the token itself). The CLI will read the token value from this env var at runtime. Example: --bearer-token-env-var MY_API_TOKEN (where MY_API_TOKEN env var contains the token)", + "id": "bearer_token_env_var", + "long": "bearer-token-env-var", + "required": false, + "short": null + }, + { + "help": "Extra HTTP headers (`KEY=VALUE`). Repeatable. Do not put secrets in the value — prefer `env:VAR` style refs or `--bearer-token-env-var`", + "id": "headers", + "long": "header", + "required": false, + "short": null + }, + { + "help": "URL for an SSE (Server-Sent Events) MCP server. Use this for MCP servers that communicate via SSE transport", + "id": "sse_url", + "long": "sse", + "required": false, + "short": null + }, + { + "help": "Optional environment variable to read for a bearer token", + "id": "sse_bearer_token_env_var", + "long": "sse-bearer-token-env-var", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "add", + "subcommands": [] + }, + { + "about": "Remove a global MCP server entry", + "arguments": [ + { + "help": "Name of the MCP server configuration to remove", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Skip confirmation prompt. Aliases: --force, -f (for compatibility)", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "remove", + "subcommands": [] + }, + { + "about": "Enable a disabled MCP server", + "arguments": [ + { + "help": "Name of the MCP server to enable", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "enable", + "subcommands": [] + }, + { + "about": "Disable an MCP server without removing it", + "arguments": [ + { + "help": "Name of the MCP server to disable", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "disable", + "subcommands": [] + }, + { + "about": "Rename an MCP server", + "arguments": [ + { + "help": "Current name of the MCP server", + "id": "old_name", + "long": null, + "required": true, + "short": null + }, + { + "help": "New name for the MCP server", + "id": "new_name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "rename", + "subcommands": [] + }, + { + "about": "Authenticate with an OAuth-enabled MCP server", + "arguments": [ + { + "help": "Name of the MCP server to authenticate with (if no subcommand)", + "id": "name", + "long": null, + "required": false, + "short": null + }, + { + "help": "Client ID for OAuth (if not using dynamic registration)", + "id": "client_id", + "long": "client-id", + "required": false, + "short": null + }, + { + "help": "Client secret for OAuth (if required)", + "id": "client_secret", + "long": "client-secret", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "auth", + "subcommands": [ + { + "about": "List OAuth status for all servers", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "List OAuth status for all servers", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Remove OAuth credentials for an MCP server", + "arguments": [ + { + "help": "Name of the MCP server to remove credentials for", + "id": "name", + "long": null, + "required": false, + "short": null + }, + { + "help": "Remove OAuth credentials for all servers", + "id": "all", + "long": "all", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "logout", + "subcommands": [] + }, + { + "about": "Debug and test an MCP server connection", + "arguments": [ + { + "help": "Name of the MCP server to debug", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Test OAuth authentication if configured", + "id": "test_auth", + "long": "test-auth", + "required": false, + "short": null + }, + { + "help": "Timeout in seconds for connection test", + "id": "timeout", + "long": "timeout", + "required": false, + "short": null + }, + { + "help": "Force fresh health check, bypassing any cache. By default, health checks may be cached for performance. Use this flag to ensure you get the current status after making configuration changes", + "id": "no_cache", + "long": "no-cache", + "required": false, + "short": null + }, + { + "help": "Show cache information when displaying cached results. Displays the age of cached health status if available", + "id": "show_cache_info", + "long": "show-cache-info", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "debug", + "subcommands": [] + }, + { + "about": "List tools exposed by a configured MCP server", + "arguments": [ + { + "help": "Name of the MCP server whose tools to list", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Timeout in seconds for the connection", + "id": "timeout", + "long": "timeout", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "tools", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "List configured MCP servers", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Show details for a configured MCP server", + "arguments": [], + "name": "get", + "subcommands": [] + }, + { + "about": "Add a global MCP server entry", + "arguments": [], + "name": "add", + "subcommands": [] + }, + { + "about": "Remove a global MCP server entry", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "Enable a disabled MCP server", + "arguments": [], + "name": "enable", + "subcommands": [] + }, + { + "about": "Disable an MCP server without removing it", + "arguments": [], + "name": "disable", + "subcommands": [] + }, + { + "about": "Rename an MCP server", + "arguments": [], + "name": "rename", + "subcommands": [] + }, + { + "about": "Authenticate with an OAuth-enabled MCP server", + "arguments": [], + "name": "auth", + "subcommands": [ + { + "about": "List OAuth status for all servers", + "arguments": [], + "name": "list", + "subcommands": [] + } + ] + }, + { + "about": "Remove OAuth credentials for an MCP server", + "arguments": [], + "name": "logout", + "subcommands": [] + }, + { + "about": "Debug and test an MCP server connection", + "arguments": [], + "name": "debug", + "subcommands": [] + }, + { + "about": "List tools exposed by a configured MCP server", + "arguments": [], + "name": "tools", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Run the MCP server (stdio transport)", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "mcp-server", + "subcommands": [] + }, + { + "about": "Start ACP server for IDE integration (e.g., Zed)", + "arguments": [ + { + "help": "Working directory for the session", + "id": "cwd", + "long": "cwd", + "required": false, + "short": "C" + }, + { + "help": "Port to listen on (default: random available port). If 0, uses stdio transport instead of HTTP", + "id": "port", + "long": "port", + "required": false, + "short": "p" + }, + { + "help": "Host address to bind to", + "id": "host", + "long": "host", + "required": false, + "short": null + }, + { + "help": "Use stdio transport (JSON-RPC over stdin/stdout)", + "id": "stdio", + "long": "stdio", + "required": false, + "short": null + }, + { + "help": "Enable verbose/debug output", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Model to use", + "id": "model", + "long": "model", + "required": false, + "short": "m" + }, + { + "help": "Agent to use", + "id": "agent", + "long": "agent", + "required": false, + "short": null + }, + { + "help": "Tools to allow (whitelist). Can be specified multiple times. Only these tools will be available to the agent", + "id": "allow_tools", + "long": "allow-tool", + "required": false, + "short": null + }, + { + "help": "Tools to deny (blacklist). Can be specified multiple times. These tools will be blocked from use", + "id": "deny_tools", + "long": "deny-tool", + "required": false, + "short": null + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "acp", + "subcommands": [] + }, + { + "about": "Show or edit configuration", + "arguments": [ + { + "help": "Show configuration in JSON format", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Edit configuration interactively", + "id": "edit", + "long": "edit", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "config", + "subcommands": [ + { + "about": "Get a configuration value", + "arguments": [ + { + "help": "Configuration key to get (e.g., model, provider)", + "id": "key", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "get", + "subcommands": [] + }, + { + "about": "Set a configuration value", + "arguments": [ + { + "help": "Configuration key (e.g., model, provider)", + "id": "key", + "long": null, + "required": true, + "short": null + }, + { + "help": "Value to set", + "id": "value", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "set", + "subcommands": [] + }, + { + "about": "Unset (remove) a configuration value", + "arguments": [ + { + "help": "Configuration key to remove", + "id": "key", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "unset", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Get a configuration value", + "arguments": [], + "name": "get", + "subcommands": [] + }, + { + "about": "Set a configuration value", + "arguments": [], + "name": "set", + "subcommands": [] + }, + { + "about": "Unset (remove) a configuration value", + "arguments": [], + "name": "unset", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "List available models", + "arguments": [ + { + "help": "Filter by provider name", + "id": "provider", + "long": null, + "required": false, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "models", + "subcommands": [ + { + "about": "List all available models", + "arguments": [ + { + "help": "Filter by provider name", + "id": "provider", + "long": null, + "required": false, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Limit number of results (for pagination)", + "id": "limit", + "long": "limit", + "required": false, + "short": null + }, + { + "help": "Offset for pagination (skip first N models)", + "id": "offset", + "long": "offset", + "required": false, + "short": null + }, + { + "help": "Sort order for models (id, name, provider) (Issue #1993)", + "id": "sort", + "long": "sort", + "required": false, + "short": null + }, + { + "help": "Show full model IDs without truncation (Issue #1991)", + "id": "full", + "long": "full", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "List all available models", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Inspect feature flags", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "features", + "subcommands": [ + { + "about": "List known features with their stage and effective state", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "List known features with their stage and effective state", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Initialize AGENTS.md in the current directory", + "arguments": [ + { + "help": "Force overwrite if AGENTS.md already exists", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Accept defaults without prompting (non-interactive mode)", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "init", + "subcommands": [] + }, + { + "about": "GitHub integration (actions, workflows)", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "github", + "subcommands": [ + { + "about": "Install GitHub Actions workflow for Cortex CI/CD automation", + "arguments": [ + { + "help": "Path to the repository root (defaults to current directory)", + "id": "path", + "long": "path", + "required": false, + "short": "p" + }, + { + "help": "Force overwrite existing workflow file", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Include PR review automation in the workflow. When enabled, the agent will: - Automatically review new and updated pull requests - Analyze code changes for bugs, security issues, and best practices - Suggest improvements with inline comments - Respond to review comments and questions", + "id": "pr_review", + "long": "pr-review", + "required": false, + "short": null + }, + { + "help": "Include issue automation", + "id": "issue_automation", + "long": "issue-automation", + "required": false, + "short": null + }, + { + "help": "Custom workflow name", + "id": "workflow_name", + "long": "workflow-name", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "install", + "subcommands": [] + }, + { + "about": "Run GitHub agent in Actions context", + "arguments": [ + { + "help": "GitHub event type (issue_comment, pull_request, issues, etc.)", + "id": "event", + "long": "event", + "required": true, + "short": "e" + }, + { + "help": "GitHub token for API access", + "id": "token", + "long": "token", + "required": false, + "short": "t" + }, + { + "help": "Path to the event payload JSON file", + "id": "event_path", + "long": "event-path", + "required": false, + "short": null + }, + { + "help": "GitHub repository (owner/repo format)", + "id": "repository", + "long": "repository", + "required": false, + "short": null + }, + { + "help": "GitHub run ID", + "id": "run_id", + "long": "run-id", + "required": false, + "short": null + }, + { + "help": "Dry run mode - don't execute, just show what would happen", + "id": "dry_run", + "long": "dry-run", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "run", + "subcommands": [] + }, + { + "about": "Check GitHub Actions installation status", + "arguments": [ + { + "help": "Path to the repository root (defaults to current directory)", + "id": "path", + "long": "path", + "required": false, + "short": "p" + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "status", + "subcommands": [] + }, + { + "about": "Uninstall/remove the Cortex GitHub workflow", + "arguments": [ + { + "help": "Path to the repository root (defaults to current directory)", + "id": "path", + "long": "path", + "required": false, + "short": "p" + }, + { + "help": "Workflow name to remove (defaults to \"cortex\")", + "id": "workflow_name", + "long": "workflow-name", + "required": false, + "short": null + }, + { + "help": "Force removal without confirmation", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "uninstall", + "subcommands": [] + }, + { + "about": "Update the Cortex GitHub workflow to the latest version", + "arguments": [ + { + "help": "Path to the repository root (defaults to current directory)", + "id": "path", + "long": "path", + "required": false, + "short": "p" + }, + { + "help": "Workflow name to update (defaults to \"cortex\")", + "id": "workflow_name", + "long": "workflow-name", + "required": false, + "short": null + }, + { + "help": "Include PR review automation", + "id": "pr_review", + "long": "pr-review", + "required": false, + "short": null + }, + { + "help": "Include issue automation", + "id": "issue_automation", + "long": "issue-automation", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "update", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Install GitHub Actions workflow for Cortex CI/CD automation", + "arguments": [], + "name": "install", + "subcommands": [] + }, + { + "about": "Run GitHub agent in Actions context", + "arguments": [], + "name": "run", + "subcommands": [] + }, + { + "about": "Check GitHub Actions installation status", + "arguments": [], + "name": "status", + "subcommands": [] + }, + { + "about": "Uninstall/remove the Cortex GitHub workflow", + "arguments": [], + "name": "uninstall", + "subcommands": [] + }, + { + "about": "Update the Cortex GitHub workflow to the latest version", + "arguments": [], + "name": "update", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Checkout a pull request", + "arguments": [ + { + "help": "PR number to checkout", + "id": "number", + "long": null, + "required": true, + "short": null + }, + { + "help": "Path to the repository root (defaults to current directory)", + "id": "path", + "long": "path", + "required": false, + "short": "p" + }, + { + "help": "Custom local branch name for the PR checkout. If not specified, defaults to \"pr-{number}\"", + "id": "branch", + "long": "branch", + "required": false, + "short": "b" + }, + { + "help": "Force checkout even if there are uncommitted changes. WARNING: This may result in data loss! Uncommitted changes in your working directory may be overwritten. Consider using 'git stash' first to save your work", + "id": "force", + "long": "force", + "required": false, + "short": "F" + }, + { + "help": "Show PR details without checking out", + "id": "info", + "long": "info", + "required": false, + "short": null + }, + { + "help": "Show PR diff without checking out", + "id": "diff", + "long": "diff", + "required": false, + "short": null + }, + { + "help": "Show PR comments", + "id": "comments", + "long": "comments", + "required": false, + "short": null + }, + { + "help": "Apply AI-suggested changes to working tree", + "id": "apply", + "long": "apply", + "required": false, + "short": null + }, + { + "help": "GitHub token for API access (for private repos)", + "id": "token", + "long": "token", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "pr", + "subcommands": [] + }, + { + "about": "Scrape web content to markdown/text/html", + "arguments": [ + { + "help": "URL to scrape", + "id": "url", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output file path (stdout if not specified)", + "id": "output", + "long": "output", + "required": false, + "short": "o" + }, + { + "help": "Output format (markdown, text, html)", + "id": "format", + "long": "format", + "required": false, + "short": "f" + }, + { + "help": "HTTP method to use for the request. Use HEAD to check headers without downloading content. Supported values: GET (default), HEAD, POST", + "id": "method", + "long": "method", + "required": false, + "short": null + }, + { + "help": "Request timeout in seconds (0 means no timeout)", + "id": "timeout", + "long": "timeout", + "required": false, + "short": "t" + }, + { + "help": "Number of retries for 5xx server errors (default: 3). Set to 0 to disable retries", + "id": "retries", + "long": "retries", + "required": false, + "short": null + }, + { + "help": "Custom User-Agent string to identify the request. Common examples: Mozilla/5.0 (compatible; Googlebot/2.1) - Googlebot Mozilla/5.0 (Windows NT 10.0; Win64) - Windows browser curl/7.68.0 - curl client PostmanRuntime/7.29.0 - Postman Some sites block requests from unknown user agents", + "id": "user_agent", + "long": "user-agent", + "required": false, + "short": null + }, + { + "help": "Custom headers (format: \"Header-Name: value\")", + "id": "headers", + "long": "header", + "required": false, + "short": "H" + }, + { + "help": "Cookie to send with the request (format: \"name=value\"). Can be specified multiple times for multiple cookies. Example: --cookie \"session=abc123\" --cookie \"user=john\"", + "id": "cookies", + "long": "cookie", + "required": false, + "short": null + }, + { + "help": "Disable following HTTP redirects. By default, up to 10 redirects are followed", + "id": "no_follow_redirects", + "long": "no-follow-redirects", + "required": false, + "short": null + }, + { + "help": "Strip images from output", + "id": "no_images", + "long": "no-images", + "required": false, + "short": null + }, + { + "help": "Strip links from output (keep text only)", + "id": "no_links", + "long": "no-links", + "required": false, + "short": null + }, + { + "help": "CSS selectors to extract specific elements from the page. Can be specified multiple times to combine selectors. Examples: article - Select all
elements .content - Select elements with class \"content\" #main - Select element with id \"main\" div.post > p - Select

children of div.post table tbody tr - Select table rows in tbody h1, h2, h3 - Select multiple heading levels [data-id=\"123\"] - Select by attribute Multiple selectors: --selector \"h1\" --selector \"p\"", + "id": "selector", + "long": "selector", + "required": false, + "short": null + }, + { + "help": "XPath expression to extract specific elements from the page. Examples: //article - Select all

elements //div[@class='content'] - Select div with class \"content\" //*[@id='main'] - Select element with id \"main\" //div[@class='post']/p - Select

children of div.post //table/tbody/tr - Select table rows in tbody //h1 | //h2 | //h3 - Select multiple heading levels Note: XPath support requires external parsing and may be slower than CSS", + "id": "xpath", + "long": "xpath", + "required": false, + "short": null + }, + { + "help": "Show verbose output (includes fetching info)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Pretty-print JSON and XML responses with proper formatting", + "id": "pretty", + "long": "pretty", + "required": false, + "short": null + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "scrape", + "subcommands": [] + }, + { + "about": "Show usage statistics", + "arguments": [ + { + "help": "Number of days to include (default: 30, range: 1-3650)", + "id": "days", + "long": "days", + "required": false, + "short": "d" + }, + { + "help": "Filter by specific provider", + "id": "provider", + "long": "provider", + "required": false, + "short": "p" + }, + { + "help": "Filter by specific model", + "id": "model", + "long": "model", + "required": false, + "short": "m" + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Show detailed breakdown", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "stats", + "subcommands": [] + }, + { + "about": "Generate shell completion scripts", + "arguments": [ + { + "help": "Shell to generate completions for", + "id": "shell", + "long": null, + "required": false, + "short": null + }, + { + "help": "Install completions to your shell configuration file", + "id": "install", + "long": "install", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "completion", + "subcommands": [] + }, + { + "about": "Check for and install updates", + "arguments": [ + { + "help": "Target version to upgrade to (e.g., \"1.2.0\") If not specified, upgrades to the latest version", + "id": "version", + "long": null, + "required": false, + "short": null + }, + { + "help": "Only check for updates without installing", + "id": "check", + "long": "check", + "required": false, + "short": "c" + }, + { + "help": "Show changelog for the target version", + "id": "changelog", + "long": "changelog", + "required": false, + "short": null + }, + { + "help": "Force upgrade even if already on the target version", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Skip confirmation prompts", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Release channel to use (stable, beta, nightly)", + "id": "channel", + "long": "channel", + "required": false, + "short": null + }, + { + "help": "Include prerelease versions (shorthand for --channel beta). When specified, allows installing beta/prerelease versions", + "id": "pre", + "long": "pre", + "required": false, + "short": null + }, + { + "help": "Use custom software distribution URL", + "id": "url", + "long": "url", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "upgrade", + "subcommands": [] + }, + { + "about": "Uninstall Cortex CLI", + "arguments": [ + { + "help": "Keep configuration files (config.toml, etc.)", + "id": "keep_config", + "long": "keep-config", + "required": false, + "short": "c" + }, + { + "help": "Keep session data and history", + "id": "keep_data", + "long": "keep-data", + "required": false, + "short": "d" + }, + { + "help": "Show what would be deleted without actually deleting", + "id": "dry_run", + "long": "dry-run", + "required": false, + "short": null + }, + { + "help": "Skip confirmation prompt", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Auto-confirm (alias for --force)", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Create backup before uninstalling", + "id": "backup", + "long": "backup", + "required": false, + "short": null + }, + { + "help": "Complete removal: delete everything including config and data. This is equivalent to not using --keep-config or --keep-data", + "id": "purge", + "long": "purge", + "required": false, + "short": "p" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "uninstall", + "subcommands": [] + }, + { + "about": "Data compaction and cleanup (logs, sessions, history)", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "compact", + "subcommands": [ + { + "about": "Run a compaction cycle (logs + sessions)", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Dry run - show what would be done without actually doing it", + "id": "dry_run", + "long": "dry-run", + "required": false, + "short": null + }, + { + "help": "Force compaction even if lock is held (use with caution)", + "id": "force", + "long": "force", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "run", + "subcommands": [] + }, + { + "about": "Prune old log files", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Dry run - show what would be deleted without actually deleting", + "id": "dry_run", + "long": "dry-run", + "required": false, + "short": null + }, + { + "help": "Keep logs from last N days (default: 7)", + "id": "keep_days", + "long": "keep-days", + "required": false, + "short": null + }, + { + "help": "Maximum log file size in MB for rotation (default: 10)", + "id": "max_size_mb", + "long": "max-size-mb", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "logs", + "subcommands": [] + }, + { + "about": "Vacuum session database (clean orphaned files)", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Dry run - show what would be cleaned without actually cleaning", + "id": "dry_run", + "long": "dry-run", + "required": false, + "short": null + }, + { + "help": "Delete sessions older than N days (0 = keep all)", + "id": "session_days", + "long": "session-days", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "vacuum", + "subcommands": [] + }, + { + "about": "Show compaction status and statistics", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "status", + "subcommands": [] + }, + { + "about": "Configure auto-compaction settings", + "arguments": [ + { + "help": "Enable auto-compaction", + "id": "enable", + "long": "enable", + "required": false, + "short": null + }, + { + "help": "Disable auto-compaction", + "id": "disable", + "long": "disable", + "required": false, + "short": null + }, + { + "help": "Set compaction interval in hours", + "id": "interval_hours", + "long": "interval-hours", + "required": false, + "short": null + }, + { + "help": "Set log retention period in days", + "id": "log_retention_days", + "long": "log-retention-days", + "required": false, + "short": null + }, + { + "help": "Output current config as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "config", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Run a compaction cycle (logs + sessions)", + "arguments": [], + "name": "run", + "subcommands": [] + }, + { + "about": "Prune old log files", + "arguments": [], + "name": "logs", + "subcommands": [] + }, + { + "about": "Vacuum session database (clean orphaned files)", + "arguments": [], + "name": "vacuum", + "subcommands": [] + }, + { + "about": "Show compaction status and statistics", + "arguments": [], + "name": "status", + "subcommands": [] + }, + { + "about": "Configure auto-compaction settings", + "arguments": [], + "name": "config", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Manage cache", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "cache", + "subcommands": [ + { + "about": "Show cache information and statistics", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "show", + "subcommands": [] + }, + { + "about": "Clear all or part of the cache", + "arguments": [ + { + "help": "Clear only model cache", + "id": "models", + "long": "models", + "required": false, + "short": null + }, + { + "help": "Clear only response cache", + "id": "responses", + "long": "responses", + "required": false, + "short": null + }, + { + "help": "Clear only update check cache", + "id": "updates", + "long": "updates", + "required": false, + "short": null + }, + { + "help": "Skip confirmation prompt", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Dry run - show what would be deleted without actually deleting", + "id": "dry_run", + "long": "dry-run", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "clear", + "subcommands": [] + }, + { + "about": "Show cache size", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "size", + "subcommands": [] + }, + { + "about": "List cached items", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Maximum number of items to show", + "id": "limit", + "long": "limit", + "required": false, + "short": "n" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Show cache information and statistics", + "arguments": [], + "name": "show", + "subcommands": [] + }, + { + "about": "Clear all or part of the cache", + "arguments": [], + "name": "clear", + "subcommands": [] + }, + { + "about": "Show cache size", + "arguments": [], + "name": "size", + "subcommands": [] + }, + { + "about": "List cached items", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "View application logs", + "arguments": [ + { + "help": "Number of lines to show (default: 100)", + "id": "lines", + "long": "lines", + "required": false, + "short": "n" + }, + { + "help": "Follow logs in real-time (like tail -f)", + "id": "follow", + "long": "follow", + "required": false, + "short": "f" + }, + { + "help": "Filter by log level (error, warn, info, debug, trace)", + "id": "level", + "long": "level", + "required": false, + "short": "l" + }, + { + "help": "Show logs from a specific session", + "id": "session", + "long": "session", + "required": false, + "short": "s" + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Show log file paths instead of content", + "id": "paths", + "long": "paths", + "required": false, + "short": null + }, + { + "help": "Clear old log files", + "id": "clear", + "long": "clear", + "required": false, + "short": null + }, + { + "help": "Keep logs from last N days when clearing (default: 7)", + "id": "keep_days", + "long": "keep-days", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "logs", + "subcommands": [] + }, + { + "about": "Submit feedback and bug reports", + "arguments": [ + { + "help": "Feedback message (if no subcommand)", + "id": "message", + "long": null, + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "feedback", + "subcommands": [ + { + "about": "Report a bug", + "arguments": [ + { + "help": "Bug description", + "id": "description", + "long": null, + "required": false, + "short": null + }, + { + "help": "Include recent logs with the report", + "id": "include_logs", + "long": "include-logs", + "required": false, + "short": null + }, + { + "help": "Session ID to attach to the bug report", + "id": "session", + "long": "session", + "required": false, + "short": "s" + }, + { + "help": "Output as JSON (for scripting)", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "bug", + "subcommands": [] + }, + { + "about": "Report a good AI result", + "arguments": [ + { + "help": "Comment about the result", + "id": "comment", + "long": null, + "required": false, + "short": null + }, + { + "help": "Session ID to attach", + "id": "session", + "long": "session", + "required": false, + "short": "s" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "good", + "subcommands": [] + }, + { + "about": "Report a bad AI result", + "arguments": [ + { + "help": "Comment about the result", + "id": "comment", + "long": null, + "required": false, + "short": null + }, + { + "help": "Session ID to attach", + "id": "session", + "long": "session", + "required": false, + "short": "s" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "bad", + "subcommands": [] + }, + { + "about": "Submit general feedback", + "arguments": [ + { + "help": "Feedback message", + "id": "message", + "long": null, + "required": false, + "short": null + }, + { + "help": "Include recent logs", + "id": "include_logs", + "long": "include-logs", + "required": false, + "short": null + }, + { + "help": "Session ID to attach", + "id": "session", + "long": "session", + "required": false, + "short": "s" + }, + { + "help": "Feedback category", + "id": "category", + "long": "category", + "required": false, + "short": "c" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "submit", + "subcommands": [] + }, + { + "about": "View feedback history", + "arguments": [ + { + "help": "Number of entries to show", + "id": "limit", + "long": "limit", + "required": false, + "short": "n" + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "history", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Report a bug", + "arguments": [], + "name": "bug", + "subcommands": [] + }, + { + "about": "Report a good AI result", + "arguments": [], + "name": "good", + "subcommands": [] + }, + { + "about": "Report a bad AI result", + "arguments": [], + "name": "bad", + "subcommands": [] + }, + { + "about": "Submit general feedback", + "arguments": [], + "name": "submit", + "subcommands": [] + }, + { + "about": "View feedback history", + "arguments": [], + "name": "history", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Lock/protect sessions from deletion", + "arguments": [ + { + "help": "Session ID to lock (if no subcommand)", + "id": "session_id", + "long": null, + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "lock", + "subcommands": [ + { + "about": "Lock a session", + "arguments": [ + { + "help": "Session ID(s) to lock", + "id": "session_ids", + "long": null, + "required": true, + "short": null + }, + { + "help": "Reason for locking", + "id": "reason", + "long": "reason", + "required": false, + "short": "r" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "add", + "subcommands": [] + }, + { + "about": "Unlock a session", + "arguments": [ + { + "help": "Session ID(s) to unlock", + "id": "session_ids", + "long": null, + "required": true, + "short": null + }, + { + "help": "Skip confirmation prompt", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "remove", + "subcommands": [] + }, + { + "about": "List locked sessions", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Check if a session is locked", + "arguments": [ + { + "help": "Session ID to check", + "id": "session_id", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "check", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Lock a session", + "arguments": [], + "name": "add", + "subcommands": [] + }, + { + "about": "Unlock a session", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "List locked sessions", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Check if a session is locked", + "arguments": [], + "name": "check", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Manage command aliases", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "alias", + "subcommands": [ + { + "about": "Set a command alias", + "arguments": [ + { + "help": "Alias name (short name for the command)", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Command to alias (e.g., \"exec --output-schema\")", + "id": "command", + "long": null, + "required": true, + "short": null + }, + { + "help": "Description of the alias", + "id": "description", + "long": "description", + "required": false, + "short": "d" + }, + { + "help": "Force overwrite if alias already exists", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "set", + "subcommands": [] + }, + { + "about": "List all aliases", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Remove an alias", + "arguments": [ + { + "help": "Alias name to remove", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Skip confirmation prompt", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "remove", + "subcommands": [] + }, + { + "about": "Show alias details", + "arguments": [ + { + "help": "Alias name to show", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "show", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Set a command alias", + "arguments": [], + "name": "set", + "subcommands": [] + }, + { + "about": "List all aliases", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Remove an alias", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "Show alias details", + "arguments": [], + "name": "show", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Manage plugins", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "plugin", + "subcommands": [ + { + "about": "List installed plugins", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Show only enabled plugins", + "id": "enabled", + "long": "enabled", + "required": false, + "short": null + }, + { + "help": "Show only disabled plugins", + "id": "disabled", + "long": "disabled", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Install a plugin", + "arguments": [ + { + "help": "Plugin name or URL to install", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Plugin version (defaults to latest)", + "id": "version", + "long": "version", + "required": false, + "short": null + }, + { + "help": "Force reinstall if already installed", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "install", + "subcommands": [] + }, + { + "about": "Remove a plugin", + "arguments": [ + { + "help": "Plugin name to remove", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Skip confirmation prompt", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "remove", + "subcommands": [] + }, + { + "about": "Enable a plugin", + "arguments": [ + { + "help": "Plugin name to enable", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "enable", + "subcommands": [] + }, + { + "about": "Disable a plugin", + "arguments": [ + { + "help": "Plugin name to disable", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "disable", + "subcommands": [] + }, + { + "about": "Show plugin information", + "arguments": [ + { + "help": "Plugin name to show", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "show", + "subcommands": [] + }, + { + "about": "Create a new plugin project", + "arguments": [ + { + "help": "Plugin name (will be used as directory name and ID)", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Plugin description", + "id": "description", + "long": "description", + "required": false, + "short": "d" + }, + { + "help": "Plugin author", + "id": "author", + "long": "author", + "required": false, + "short": "a" + }, + { + "help": "Output directory (defaults to current directory)", + "id": "output", + "long": "output", + "required": false, + "short": "o" + }, + { + "help": "Use advanced template with TUI hooks", + "id": "advanced", + "long": "advanced", + "required": false, + "short": null + }, + { + "help": "Use TypeScript template instead of Rust", + "id": "typescript", + "long": "typescript", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "new", + "subcommands": [] + }, + { + "about": "Start development mode with hot-reload", + "arguments": [ + { + "help": "Plugin directory (defaults to current directory)", + "id": "path", + "long": "path", + "required": false, + "short": "p" + }, + { + "help": "Watch for file changes and auto-rebuild", + "id": "watch", + "long": "watch", + "required": false, + "short": "w" + }, + { + "help": "Debounce time in milliseconds for file change events", + "id": "debounce_ms", + "long": "debounce-ms", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "dev", + "subcommands": [] + }, + { + "about": "Build the plugin WASM file", + "arguments": [ + { + "help": "Plugin directory (defaults to current directory)", + "id": "path", + "long": "path", + "required": false, + "short": "p" + }, + { + "help": "Build in debug mode (faster, larger output)", + "id": "debug", + "long": "debug", + "required": false, + "short": null + }, + { + "help": "Output directory for the compiled WASM file", + "id": "output", + "long": "output", + "required": false, + "short": "o" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "build", + "subcommands": [] + }, + { + "about": "Validate plugin manifest and structure", + "arguments": [ + { + "help": "Plugin directory (defaults to current directory)", + "id": "path", + "long": "path", + "required": false, + "short": "p" + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Show verbose output with all checks", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "validate", + "subcommands": [] + }, + { + "about": "Prepare plugin for publication (dry-run)", + "arguments": [ + { + "help": "Plugin directory (defaults to current directory)", + "id": "path", + "long": "path", + "required": false, + "short": "p" + }, + { + "help": "Dry-run mode (default, no actual publishing)", + "id": "dry_run", + "long": "dry-run", + "required": false, + "short": null + }, + { + "help": "Output tarball path (defaults to plugin-name-version.tar.gz)", + "id": "output", + "long": "output", + "required": false, + "short": "o" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "publish", + "subcommands": [] + }, + { + "about": "Search the plugin registry", + "arguments": [ + { + "help": "Search query", + "id": "query", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "search", + "subcommands": [] + }, + { + "about": "Browse plugins in the registry", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "browse", + "subcommands": [] + }, + { + "about": "Update an installed plugin from the registry", + "arguments": [ + { + "help": "Plugin name to update", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "update", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "List installed plugins", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Install a plugin", + "arguments": [], + "name": "install", + "subcommands": [] + }, + { + "about": "Remove a plugin", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "Enable a plugin", + "arguments": [], + "name": "enable", + "subcommands": [] + }, + { + "about": "Disable a plugin", + "arguments": [], + "name": "disable", + "subcommands": [] + }, + { + "about": "Show plugin information", + "arguments": [], + "name": "show", + "subcommands": [] + }, + { + "about": "Create a new plugin project", + "arguments": [], + "name": "new", + "subcommands": [] + }, + { + "about": "Start development mode with hot-reload", + "arguments": [], + "name": "dev", + "subcommands": [] + }, + { + "about": "Build the plugin WASM file", + "arguments": [], + "name": "build", + "subcommands": [] + }, + { + "about": "Validate plugin manifest and structure", + "arguments": [], + "name": "validate", + "subcommands": [] + }, + { + "about": "Prepare plugin for publication (dry-run)", + "arguments": [], + "name": "publish", + "subcommands": [] + }, + { + "about": "Search the plugin registry", + "arguments": [], + "name": "search", + "subcommands": [] + }, + { + "about": "Browse plugins in the registry", + "arguments": [], + "name": "browse", + "subcommands": [] + }, + { + "about": "Update an installed plugin from the registry", + "arguments": [], + "name": "update", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Debug and diagnostic commands", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "debug", + "subcommands": [ + { + "about": "Check local configuration, storage, and required tools without network access", + "arguments": [ + { + "help": "Output the checks as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "doctor", + "subcommands": [] + }, + { + "about": "Show resolved configuration and config file locations", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Show environment variables related to Cortex", + "id": "env", + "long": "env", + "required": false, + "short": null + }, + { + "help": "Show diff between local project config and global config", + "id": "diff", + "long": "diff", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "config", + "subcommands": [] + }, + { + "about": "Show file metadata, MIME type, and encoding", + "arguments": [ + { + "help": "Path to the file to inspect", + "id": "path", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "file", + "subcommands": [] + }, + { + "about": "List and test LSP servers", + "arguments": [ + { + "help": "Test a specific LSP server", + "id": "server", + "long": "server", + "required": false, + "short": null + }, + { + "help": "Filter by programming language (e.g., python, rust, go)", + "id": "language", + "long": "language", + "required": false, + "short": "l" + }, + { + "help": "Test LSP connection for a specific file", + "id": "file", + "long": "file", + "required": false, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "lsp", + "subcommands": [] + }, + { + "about": "Check ripgrep availability and test search", + "arguments": [ + { + "help": "Test search with a pattern", + "id": "test", + "long": "test", + "required": false, + "short": null + }, + { + "help": "Directory to search in for test", + "id": "dir", + "long": "dir", + "required": false, + "short": null + }, + { + "help": "Offer to install ripgrep if not found", + "id": "install", + "long": "install", + "required": false, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "ripgrep", + "subcommands": [] + }, + { + "about": "Parse and validate a skill file", + "arguments": [ + { + "help": "Name or path of the skill to validate", + "id": "name", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "skill", + "subcommands": [] + }, + { + "about": "Show snapshot status and diffs", + "arguments": [ + { + "help": "Session ID to inspect snapshots for", + "id": "session", + "long": "session", + "required": false, + "short": null + }, + { + "help": "Show diff between snapshots", + "id": "diff", + "long": "diff", + "required": false, + "short": null + }, + { + "help": "Create a new snapshot of the current workspace state", + "id": "create", + "long": "create", + "required": false, + "short": null + }, + { + "help": "Restore workspace to a specific snapshot state. Requires --snapshot-id to specify which snapshot to restore", + "id": "restore", + "long": "restore", + "required": false, + "short": null + }, + { + "help": "Delete a specific snapshot. Requires --snapshot-id to specify which snapshot to delete", + "id": "delete", + "long": "delete", + "required": false, + "short": null + }, + { + "help": "Snapshot ID for restore/delete operations", + "id": "snapshot_id", + "long": "snapshot-id", + "required": false, + "short": null + }, + { + "help": "Description for the snapshot (used with --create)", + "id": "description", + "long": "description", + "required": false, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "snapshot", + "subcommands": [] + }, + { + "about": "Show all Cortex paths", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Check if write locations are accessible (useful for Docker read-only containers)", + "id": "check_writable", + "long": "check-writable", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "paths", + "subcommands": [] + }, + { + "about": "Show system information (OS, architecture, shell, etc.) for bug reports", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "system", + "subcommands": [] + }, + { + "about": "Wait for a condition (useful for scripts)", + "arguments": [ + { + "help": "Wait for LSP to be ready", + "id": "lsp_ready", + "long": "lsp-ready", + "required": false, + "short": null + }, + { + "help": "Wait for server to be ready", + "id": "server_ready", + "long": "server-ready", + "required": false, + "short": null + }, + { + "help": "Server URL to check (default: http://127.0.0.1:3000)", + "id": "server_url", + "long": "server-url", + "required": false, + "short": null + }, + { + "help": "Wait for a TCP port to be available", + "id": "port", + "long": "port", + "required": false, + "short": null + }, + { + "help": "Host to check when using --port (default: 127.0.0.1)", + "id": "host", + "long": "host", + "required": false, + "short": null + }, + { + "help": "Timeout in seconds", + "id": "timeout", + "long": "timeout", + "required": false, + "short": null + }, + { + "help": "Check interval in milliseconds", + "id": "interval", + "long": "interval", + "required": false, + "short": null + }, + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "wait", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Check local configuration, storage, and required tools without network access", + "arguments": [], + "name": "doctor", + "subcommands": [] + }, + { + "about": "Show resolved configuration and config file locations", + "arguments": [], + "name": "config", + "subcommands": [] + }, + { + "about": "Show file metadata, MIME type, and encoding", + "arguments": [], + "name": "file", + "subcommands": [] + }, + { + "about": "List and test LSP servers", + "arguments": [], + "name": "lsp", + "subcommands": [] + }, + { + "about": "Check ripgrep availability and test search", + "arguments": [], + "name": "ripgrep", + "subcommands": [] + }, + { + "about": "Parse and validate a skill file", + "arguments": [], + "name": "skill", + "subcommands": [] + }, + { + "about": "Show snapshot status and diffs", + "arguments": [], + "name": "snapshot", + "subcommands": [] + }, + { + "about": "Show all Cortex paths", + "arguments": [], + "name": "paths", + "subcommands": [] + }, + { + "about": "Show system information (OS, architecture, shell, etc.) for bug reports", + "arguments": [], + "name": "system", + "subcommands": [] + }, + { + "about": "Wait for a condition (useful for scripts)", + "arguments": [], + "name": "wait", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Start interactive shell/REPL mode", + "arguments": [ + { + "help": "Model to use (e.g., claude-sonnet-4-20250514, gpt-4o)", + "id": "model", + "long": "model", + "required": false, + "short": "m" + }, + { + "help": "Working directory override", + "id": "cwd", + "long": "cwd", + "required": false, + "short": "C" + }, + { + "help": "Configuration profile from config.toml", + "id": "config_profile", + "long": "profile", + "required": false, + "short": "p" + }, + { + "help": "Enable web search capability", + "id": "web_search", + "long": "search", + "required": false, + "short": null + }, + { + "help": "Initial prompt to start the session with", + "id": "prompt", + "long": null, + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "shell", + "subcommands": [] + }, + { + "about": "Execute and manage task DAGs (dependency graphs)", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "dag", + "subcommands": [ + { + "about": "Create a new task DAG from a specification file", + "arguments": [ + { + "help": "Path to task specification file (YAML or JSON)", + "id": "file", + "long": "file", + "required": true, + "short": "f" + }, + { + "help": "Custom DAG ID (generated if not provided)", + "id": "id", + "long": "id", + "required": false, + "short": "i" + }, + { + "help": "Validate only, don't save", + "id": "dry_run", + "long": "dry-run", + "required": false, + "short": null + }, + { + "help": "Output format", + "id": "format", + "long": "format", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "create", + "subcommands": [] + }, + { + "about": "Execute a task DAG", + "arguments": [ + { + "help": "Path to task specification file (YAML or JSON)", + "id": "file", + "long": "file", + "required": true, + "short": "f" + }, + { + "help": "Maximum concurrent tasks", + "id": "max_concurrent", + "long": "jobs", + "required": false, + "short": "j" + }, + { + "help": "Task timeout in seconds", + "id": "timeout", + "long": "timeout", + "required": false, + "short": "t" + }, + { + "help": "Execution strategy", + "id": "strategy", + "long": "strategy", + "required": false, + "short": "s" + }, + { + "help": "Failure handling mode", + "id": "failure_mode", + "long": "on-failure", + "required": false, + "short": null + }, + { + "help": "Output format", + "id": "format", + "long": "format", + "required": false, + "short": null + }, + { + "help": "Quiet mode - minimal output", + "id": "quiet", + "long": "quiet", + "required": false, + "short": "q" + }, + { + "help": "Verbose mode - show task details", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Save DAG state for later resume", + "id": "save", + "long": "save", + "required": false, + "short": null + }, + { + "help": "Custom DAG ID for saving", + "id": "id", + "long": "id", + "required": false, + "short": null + }, + { + "help": "Infer task dependencies from affected files", + "id": "infer_deps", + "long": "infer-deps", + "required": false, + "short": null + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "run", + "subcommands": [] + }, + { + "about": "Show the status of a DAG", + "arguments": [ + { + "help": "DAG ID to check", + "id": "id", + "long": null, + "required": true, + "short": null + }, + { + "help": "Output format", + "id": "format", + "long": "format", + "required": false, + "short": null + }, + { + "help": "Show detailed task information", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "status", + "subcommands": [] + }, + { + "about": "List all DAGs", + "arguments": [ + { + "help": "Filter by status (pending, running, completed, failed)", + "id": "status", + "long": "status", + "required": false, + "short": null + }, + { + "help": "Output format", + "id": "format", + "long": "format", + "required": false, + "short": null + }, + { + "help": "Maximum number of DAGs to show", + "id": "limit", + "long": "limit", + "required": false, + "short": "n" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "list", + "subcommands": [] + }, + { + "about": "Validate a DAG specification", + "arguments": [ + { + "help": "Path to task specification file", + "id": "file", + "long": "file", + "required": true, + "short": "f" + }, + { + "help": "Show validation details", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "validate", + "subcommands": [] + }, + { + "about": "Visualize a DAG structure", + "arguments": [ + { + "help": "Path to task specification file", + "id": "file", + "long": "file", + "required": true, + "short": "f" + }, + { + "help": "Output format (ascii, dot, mermaid)", + "id": "output", + "long": "output", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "graph", + "subcommands": [] + }, + { + "about": "Delete a DAG", + "arguments": [ + { + "help": "DAG ID to delete", + "id": "id", + "long": null, + "required": true, + "short": null + }, + { + "help": "Skip confirmation prompt", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "delete", + "subcommands": [] + }, + { + "about": "Resume a partially executed DAG", + "arguments": [ + { + "help": "DAG ID to resume", + "id": "id", + "long": null, + "required": true, + "short": null + }, + { + "help": "Maximum concurrent tasks", + "id": "max_concurrent", + "long": "jobs", + "required": false, + "short": "j" + }, + { + "help": "Task timeout in seconds", + "id": "timeout", + "long": "timeout", + "required": false, + "short": "t" + }, + { + "help": "Failure handling mode", + "id": "failure_mode", + "long": "on-failure", + "required": false, + "short": null + }, + { + "help": "Output format", + "id": "format", + "long": "format", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "resume", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Create a new task DAG from a specification file", + "arguments": [], + "name": "create", + "subcommands": [] + }, + { + "about": "Execute a task DAG", + "arguments": [], + "name": "run", + "subcommands": [] + }, + { + "about": "Show the status of a DAG", + "arguments": [], + "name": "status", + "subcommands": [] + }, + { + "about": "List all DAGs", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Validate a DAG specification", + "arguments": [], + "name": "validate", + "subcommands": [] + }, + { + "about": "Visualize a DAG structure", + "arguments": [], + "name": "graph", + "subcommands": [] + }, + { + "about": "Delete a DAG", + "arguments": [], + "name": "delete", + "subcommands": [] + }, + { + "about": "Resume a partially executed DAG", + "arguments": [], + "name": "resume", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Discover Cortex servers on the local network", + "arguments": [ + { + "help": "Timeout for discovery in seconds", + "id": "timeout", + "long": "timeout", + "required": false, + "short": "t" + }, + { + "help": "Output in JSON format", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "servers", + "subcommands": [ + { + "about": "Re-scan the network for mDNS servers (forces a fresh discovery)", + "arguments": [ + { + "help": "Timeout for discovery in seconds", + "id": "timeout", + "long": "timeout", + "required": false, + "short": "t" + }, + { + "help": "Output in JSON format", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "refresh", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Re-scan the network for mDNS servers (forces a fresh discovery)", + "arguments": [], + "name": "refresh", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "View prompt history from past sessions", + "arguments": [ + { + "help": "Maximum number of entries to show", + "id": "limit", + "long": "limit", + "required": false, + "short": "n" + }, + { + "help": "Show history from all directories", + "id": "all", + "long": "all", + "required": false, + "short": null + }, + { + "help": "Output in JSON format", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "history", + "subcommands": [ + { + "about": "Search history for a pattern", + "arguments": [ + { + "help": "Pattern to search for in prompts", + "id": "pattern", + "long": null, + "required": true, + "short": null + }, + { + "help": "Maximum number of results", + "id": "limit", + "long": "limit", + "required": false, + "short": "n" + }, + { + "help": "Output in JSON format", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "search", + "subcommands": [] + }, + { + "about": "Clear history (requires confirmation)", + "arguments": [ + { + "help": "Skip confirmation prompt", + "id": "yes", + "long": "yes", + "required": false, + "short": "y" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "clear", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Search history for a pattern", + "arguments": [], + "name": "search", + "subcommands": [] + }, + { + "about": "Clear history (requires confirmation)", + "arguments": [], + "name": "clear", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Manage workspace/project settings", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "workspace", + "subcommands": [ + { + "about": "Show workspace information", + "arguments": [ + { + "help": "Output as JSON", + "id": "json", + "long": "json", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "show", + "subcommands": [] + }, + { + "about": "Initialize workspace configuration", + "arguments": [ + { + "help": "Force overwrite if config already exists", + "id": "force", + "long": "force", + "required": false, + "short": "f" + }, + { + "help": "Template to use (minimal, default, full)", + "id": "template", + "long": "template", + "required": false, + "short": "t" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "init", + "subcommands": [] + }, + { + "about": "Set workspace settings", + "arguments": [ + { + "help": "Configuration key to set", + "id": "key", + "long": null, + "required": true, + "short": null + }, + { + "help": "Value to set", + "id": "value", + "long": null, + "required": true, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "set", + "subcommands": [] + }, + { + "about": "Open workspace configuration in editor", + "arguments": [ + { + "help": "Editor to use (defaults to $EDITOR or $VISUAL)", + "id": "editor", + "long": "editor", + "required": false, + "short": "e" + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "edit", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Show workspace information", + "arguments": [], + "name": "show", + "subcommands": [] + }, + { + "about": "Initialize workspace configuration", + "arguments": [], + "name": "init", + "subcommands": [] + }, + { + "about": "Set workspace settings", + "arguments": [], + "name": "set", + "subcommands": [] + }, + { + "about": "Open workspace configuration in editor", + "arguments": [], + "name": "edit", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Run commands within a Cortex-provided sandbox", + "arguments": [ + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "sandbox", + "subcommands": [ + { + "about": "Run a command under Seatbelt (macOS only)", + "arguments": [ + { + "help": "Convenience alias for low-friction sandboxed automatic execution", + "id": "full_auto", + "long": "full-auto", + "required": false, + "short": null + }, + { + "help": "While the command runs, capture macOS sandbox denials", + "id": "log_denials", + "long": "log-denials", + "required": false, + "short": null + }, + { + "help": "Full command args to run under seatbelt", + "id": "command", + "long": null, + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "macos", + "subcommands": [] + }, + { + "about": "Run a command under Landlock+seccomp (Linux only)", + "arguments": [ + { + "help": "Convenience alias for low-friction sandboxed automatic execution", + "id": "full_auto", + "long": "full-auto", + "required": false, + "short": null + }, + { + "help": "Full command args to run under landlock", + "id": "command", + "long": null, + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "linux", + "subcommands": [] + }, + { + "about": "Run a command under Windows restricted token (Windows only)", + "arguments": [ + { + "help": "Convenience alias for low-friction sandboxed automatic execution", + "id": "full_auto", + "long": "full-auto", + "required": false, + "short": null + }, + { + "help": "Full command args to run under Windows restricted token sandbox", + "id": "command", + "long": null, + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "windows", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Run a command under Seatbelt (macOS only)", + "arguments": [], + "name": "macos", + "subcommands": [] + }, + { + "about": "Run a command under Landlock+seccomp (Linux only)", + "arguments": [], + "name": "linux", + "subcommands": [] + }, + { + "about": "Run a command under Windows restricted token (Windows only)", + "arguments": [], + "name": "windows", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] + }, + { + "about": "Run the HTTP API server (for desktop/web integration)", + "arguments": [ + { + "help": "Port to listen on", + "id": "port", + "long": "port", + "required": false, + "short": "p" + }, + { + "help": "Host address to bind the server to", + "id": "host", + "long": "host", + "required": false, + "short": null + }, + { + "help": "Authentication token for API access", + "id": "auth_token", + "long": "auth-token", + "required": false, + "short": null + }, + { + "help": "Enable CORS (Cross-Origin Resource Sharing) for all origins", + "id": "cors", + "long": "cors", + "required": false, + "short": null + }, + { + "help": "Allowed CORS origin(s). Can be specified multiple times", + "id": "cors_origins", + "long": "cors-origin", + "required": false, + "short": null + }, + { + "help": "Enable mDNS service discovery (advertise on local network)", + "id": "mdns", + "long": "mdns", + "required": false, + "short": null + }, + { + "help": "Disable mDNS service discovery", + "id": "no_mdns", + "long": "no-mdns", + "required": false, + "short": null + }, + { + "help": "Custom service name for mDNS advertising", + "id": "mdns_name", + "long": "mdns-name", + "required": false, + "short": null + }, + { + "help": "Enable verbose output (same as --log-level debug)", + "id": "verbose", + "long": "verbose", + "required": false, + "short": "v" + }, + { + "help": "Enable trace-level logging for debugging", + "id": "trace", + "long": "trace", + "required": false, + "short": null + }, + { + "help": "Control color output: auto (default), always, or never", + "id": "color", + "long": "color", + "required": false, + "short": null + }, + { + "help": "Print help (see more with '--help')", + "id": "help", + "long": "help", + "required": false, + "short": "h" + } + ], + "name": "serve", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [ + { + "about": "Run Cortex non-interactively with advanced options", + "arguments": [], + "name": "run", + "subcommands": [] + }, + { + "about": "Execute in headless mode (for CI/CD, scripts, automation)", + "arguments": [], + "name": "exec", + "subcommands": [] + }, + { + "about": "Resume a previous interactive session", + "arguments": [], + "name": "resume", + "subcommands": [] + }, + { + "about": "List previous sessions", + "arguments": [], + "name": "sessions", + "subcommands": [] + }, + { + "about": "Export a session to JSON format", + "arguments": [], + "name": "export", + "subcommands": [] + }, + { + "about": "Import a session from JSON file or URL", + "arguments": [], + "name": "import", + "subcommands": [] + }, + { + "about": "Delete a session", + "arguments": [], + "name": "delete", + "subcommands": [] + }, + { + "about": "Authenticate with Cortex API", + "arguments": [], + "name": "login", + "subcommands": [ + { + "about": "Show login status", + "arguments": [], + "name": "status", + "subcommands": [] + } + ] + }, + { + "about": "Remove stored authentication credentials", + "arguments": [], + "name": "logout", + "subcommands": [] + }, + { + "about": "Show currently authenticated user", + "arguments": [], + "name": "whoami", + "subcommands": [] + }, + { + "about": "Manage agents (list, create, show)", + "arguments": [], + "name": "agent", + "subcommands": [ + { + "about": "List all available agents", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Show details for a specific agent", + "arguments": [], + "name": "show", + "subcommands": [] + }, + { + "about": "Create a new agent interactively", + "arguments": [], + "name": "create", + "subcommands": [] + }, + { + "about": "Edit an existing agent in your default editor", + "arguments": [], + "name": "edit", + "subcommands": [] + }, + { + "about": "Remove a user-defined agent", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "Install an agent from the registry", + "arguments": [], + "name": "install", + "subcommands": [] + }, + { + "about": "Copy/clone an existing agent with a new name", + "arguments": [], + "name": "copy", + "subcommands": [] + }, + { + "about": "Export an agent definition to stdout or a file", + "arguments": [], + "name": "export", + "subcommands": [] + } + ] + }, + { + "about": "Manage MCP (Model Context Protocol) servers", + "arguments": [], + "name": "mcp", + "subcommands": [ + { + "about": "List configured MCP servers", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Show details for a configured MCP server", + "arguments": [], + "name": "get", + "subcommands": [] + }, + { + "about": "Add a global MCP server entry", + "arguments": [], + "name": "add", + "subcommands": [] + }, + { + "about": "Remove a global MCP server entry", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "Enable a disabled MCP server", + "arguments": [], + "name": "enable", + "subcommands": [] + }, + { + "about": "Disable an MCP server without removing it", + "arguments": [], + "name": "disable", + "subcommands": [] + }, + { + "about": "Rename an MCP server", + "arguments": [], + "name": "rename", + "subcommands": [] + }, + { + "about": "Authenticate with an OAuth-enabled MCP server", + "arguments": [], + "name": "auth", + "subcommands": [ + { + "about": "List OAuth status for all servers", + "arguments": [], + "name": "list", + "subcommands": [] + } + ] + }, + { + "about": "Remove OAuth credentials for an MCP server", + "arguments": [], + "name": "logout", + "subcommands": [] + }, + { + "about": "Debug and test an MCP server connection", + "arguments": [], + "name": "debug", + "subcommands": [] + }, + { + "about": "List tools exposed by a configured MCP server", + "arguments": [], + "name": "tools", + "subcommands": [] + } + ] + }, + { + "about": "Run the MCP server (stdio transport)", + "arguments": [], + "name": "mcp-server", + "subcommands": [] + }, + { + "about": "Start ACP server for IDE integration (e.g., Zed)", + "arguments": [], + "name": "acp", + "subcommands": [] + }, + { + "about": "Show or edit configuration", + "arguments": [], + "name": "config", + "subcommands": [ + { + "about": "Get a configuration value", + "arguments": [], + "name": "get", + "subcommands": [] + }, + { + "about": "Set a configuration value", + "arguments": [], + "name": "set", + "subcommands": [] + }, + { + "about": "Unset (remove) a configuration value", + "arguments": [], + "name": "unset", + "subcommands": [] + } + ] + }, + { + "about": "List available models", + "arguments": [], + "name": "models", + "subcommands": [ + { + "about": "List all available models", + "arguments": [], + "name": "list", + "subcommands": [] + } + ] + }, + { + "about": "Inspect feature flags", + "arguments": [], + "name": "features", + "subcommands": [ + { + "about": "List known features with their stage and effective state", + "arguments": [], + "name": "list", + "subcommands": [] + } + ] + }, + { + "about": "Initialize AGENTS.md in the current directory", + "arguments": [], + "name": "init", + "subcommands": [] + }, + { + "about": "GitHub integration (actions, workflows)", + "arguments": [], + "name": "github", + "subcommands": [ + { + "about": "Install GitHub Actions workflow for Cortex CI/CD automation", + "arguments": [], + "name": "install", + "subcommands": [] + }, + { + "about": "Run GitHub agent in Actions context", + "arguments": [], + "name": "run", + "subcommands": [] + }, + { + "about": "Check GitHub Actions installation status", + "arguments": [], + "name": "status", + "subcommands": [] + }, + { + "about": "Uninstall/remove the Cortex GitHub workflow", + "arguments": [], + "name": "uninstall", + "subcommands": [] + }, + { + "about": "Update the Cortex GitHub workflow to the latest version", + "arguments": [], + "name": "update", + "subcommands": [] + } + ] + }, + { + "about": "Checkout a pull request", + "arguments": [], + "name": "pr", + "subcommands": [] + }, + { + "about": "Scrape web content to markdown/text/html", + "arguments": [], + "name": "scrape", + "subcommands": [] + }, + { + "about": "Show usage statistics", + "arguments": [], + "name": "stats", + "subcommands": [] + }, + { + "about": "Generate shell completion scripts", + "arguments": [], + "name": "completion", + "subcommands": [] + }, + { + "about": "Check for and install updates", + "arguments": [], + "name": "upgrade", + "subcommands": [] + }, + { + "about": "Uninstall Cortex CLI", + "arguments": [], + "name": "uninstall", + "subcommands": [] + }, + { + "about": "Data compaction and cleanup (logs, sessions, history)", + "arguments": [], + "name": "compact", + "subcommands": [ + { + "about": "Run a compaction cycle (logs + sessions)", + "arguments": [], + "name": "run", + "subcommands": [] + }, + { + "about": "Prune old log files", + "arguments": [], + "name": "logs", + "subcommands": [] + }, + { + "about": "Vacuum session database (clean orphaned files)", + "arguments": [], + "name": "vacuum", + "subcommands": [] + }, + { + "about": "Show compaction status and statistics", + "arguments": [], + "name": "status", + "subcommands": [] + }, + { + "about": "Configure auto-compaction settings", + "arguments": [], + "name": "config", + "subcommands": [] + } + ] + }, + { + "about": "Manage cache", + "arguments": [], + "name": "cache", + "subcommands": [ + { + "about": "Show cache information and statistics", + "arguments": [], + "name": "show", + "subcommands": [] + }, + { + "about": "Clear all or part of the cache", + "arguments": [], + "name": "clear", + "subcommands": [] + }, + { + "about": "Show cache size", + "arguments": [], + "name": "size", + "subcommands": [] + }, + { + "about": "List cached items", + "arguments": [], + "name": "list", + "subcommands": [] + } + ] + }, + { + "about": "View application logs", + "arguments": [], + "name": "logs", + "subcommands": [] + }, + { + "about": "Submit feedback and bug reports", + "arguments": [], + "name": "feedback", + "subcommands": [ + { + "about": "Report a bug", + "arguments": [], + "name": "bug", + "subcommands": [] + }, + { + "about": "Report a good AI result", + "arguments": [], + "name": "good", + "subcommands": [] + }, + { + "about": "Report a bad AI result", + "arguments": [], + "name": "bad", + "subcommands": [] + }, + { + "about": "Submit general feedback", + "arguments": [], + "name": "submit", + "subcommands": [] + }, + { + "about": "View feedback history", + "arguments": [], + "name": "history", + "subcommands": [] + } + ] + }, + { + "about": "Lock/protect sessions from deletion", + "arguments": [], + "name": "lock", + "subcommands": [ + { + "about": "Lock a session", + "arguments": [], + "name": "add", + "subcommands": [] + }, + { + "about": "Unlock a session", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "List locked sessions", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Check if a session is locked", + "arguments": [], + "name": "check", + "subcommands": [] + } + ] + }, + { + "about": "Manage command aliases", + "arguments": [], + "name": "alias", + "subcommands": [ + { + "about": "Set a command alias", + "arguments": [], + "name": "set", + "subcommands": [] + }, + { + "about": "List all aliases", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Remove an alias", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "Show alias details", + "arguments": [], + "name": "show", + "subcommands": [] + } + ] + }, + { + "about": "Manage plugins", + "arguments": [], + "name": "plugin", + "subcommands": [ + { + "about": "List installed plugins", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Install a plugin", + "arguments": [], + "name": "install", + "subcommands": [] + }, + { + "about": "Remove a plugin", + "arguments": [], + "name": "remove", + "subcommands": [] + }, + { + "about": "Enable a plugin", + "arguments": [], + "name": "enable", + "subcommands": [] + }, + { + "about": "Disable a plugin", + "arguments": [], + "name": "disable", + "subcommands": [] + }, + { + "about": "Show plugin information", + "arguments": [], + "name": "show", + "subcommands": [] + }, + { + "about": "Create a new plugin project", + "arguments": [], + "name": "new", + "subcommands": [] + }, + { + "about": "Start development mode with hot-reload", + "arguments": [], + "name": "dev", + "subcommands": [] + }, + { + "about": "Build the plugin WASM file", + "arguments": [], + "name": "build", + "subcommands": [] + }, + { + "about": "Validate plugin manifest and structure", + "arguments": [], + "name": "validate", + "subcommands": [] + }, + { + "about": "Prepare plugin for publication (dry-run)", + "arguments": [], + "name": "publish", + "subcommands": [] + }, + { + "about": "Search the plugin registry", + "arguments": [], + "name": "search", + "subcommands": [] + }, + { + "about": "Browse plugins in the registry", + "arguments": [], + "name": "browse", + "subcommands": [] + }, + { + "about": "Update an installed plugin from the registry", + "arguments": [], + "name": "update", + "subcommands": [] + } + ] + }, + { + "about": "Debug and diagnostic commands", + "arguments": [], + "name": "debug", + "subcommands": [ + { + "about": "Check local configuration, storage, and required tools without network access", + "arguments": [], + "name": "doctor", + "subcommands": [] + }, + { + "about": "Show resolved configuration and config file locations", + "arguments": [], + "name": "config", + "subcommands": [] + }, + { + "about": "Show file metadata, MIME type, and encoding", + "arguments": [], + "name": "file", + "subcommands": [] + }, + { + "about": "List and test LSP servers", + "arguments": [], + "name": "lsp", + "subcommands": [] + }, + { + "about": "Check ripgrep availability and test search", + "arguments": [], + "name": "ripgrep", + "subcommands": [] + }, + { + "about": "Parse and validate a skill file", + "arguments": [], + "name": "skill", + "subcommands": [] + }, + { + "about": "Show snapshot status and diffs", + "arguments": [], + "name": "snapshot", + "subcommands": [] + }, + { + "about": "Show all Cortex paths", + "arguments": [], + "name": "paths", + "subcommands": [] + }, + { + "about": "Show system information (OS, architecture, shell, etc.) for bug reports", + "arguments": [], + "name": "system", + "subcommands": [] + }, + { + "about": "Wait for a condition (useful for scripts)", + "arguments": [], + "name": "wait", + "subcommands": [] + } + ] + }, + { + "about": "Start interactive shell/REPL mode", + "arguments": [], + "name": "shell", + "subcommands": [] + }, + { + "about": "Execute and manage task DAGs (dependency graphs)", + "arguments": [], + "name": "dag", + "subcommands": [ + { + "about": "Create a new task DAG from a specification file", + "arguments": [], + "name": "create", + "subcommands": [] + }, + { + "about": "Execute a task DAG", + "arguments": [], + "name": "run", + "subcommands": [] + }, + { + "about": "Show the status of a DAG", + "arguments": [], + "name": "status", + "subcommands": [] + }, + { + "about": "List all DAGs", + "arguments": [], + "name": "list", + "subcommands": [] + }, + { + "about": "Validate a DAG specification", + "arguments": [], + "name": "validate", + "subcommands": [] + }, + { + "about": "Visualize a DAG structure", + "arguments": [], + "name": "graph", + "subcommands": [] + }, + { + "about": "Delete a DAG", + "arguments": [], + "name": "delete", + "subcommands": [] + }, + { + "about": "Resume a partially executed DAG", + "arguments": [], + "name": "resume", + "subcommands": [] + } + ] + }, + { + "about": "Discover Cortex servers on the local network", + "arguments": [], + "name": "servers", + "subcommands": [ + { + "about": "Re-scan the network for mDNS servers (forces a fresh discovery)", + "arguments": [], + "name": "refresh", + "subcommands": [] + } + ] + }, + { + "about": "View prompt history from past sessions", + "arguments": [], + "name": "history", + "subcommands": [ + { + "about": "Search history for a pattern", + "arguments": [], + "name": "search", + "subcommands": [] + }, + { + "about": "Clear history (requires confirmation)", + "arguments": [], + "name": "clear", + "subcommands": [] + } + ] + }, + { + "about": "Manage workspace/project settings", + "arguments": [], + "name": "workspace", + "subcommands": [ + { + "about": "Show workspace information", + "arguments": [], + "name": "show", + "subcommands": [] + }, + { + "about": "Initialize workspace configuration", + "arguments": [], + "name": "init", + "subcommands": [] + }, + { + "about": "Set workspace settings", + "arguments": [], + "name": "set", + "subcommands": [] + }, + { + "about": "Open workspace configuration in editor", + "arguments": [], + "name": "edit", + "subcommands": [] + } + ] + }, + { + "about": "Run commands within a Cortex-provided sandbox", + "arguments": [], + "name": "sandbox", + "subcommands": [ + { + "about": "Run a command under Seatbelt (macOS only)", + "arguments": [], + "name": "macos", + "subcommands": [] + }, + { + "about": "Run a command under Landlock+seccomp (Linux only)", + "arguments": [], + "name": "linux", + "subcommands": [] + }, + { + "about": "Run a command under Windows restricted token (Windows only)", + "arguments": [], + "name": "windows", + "subcommands": [] + } + ] + }, + { + "about": "Run the HTTP API server (for desktop/web integration)", + "arguments": [], + "name": "serve", + "subcommands": [] + }, + { + "about": "Print this message or the help of the given subcommand(s)", + "arguments": [], + "name": "help", + "subcommands": [] + } + ] + } + ] +} diff --git a/docs/reference/privacy.md b/docs/reference/privacy.md new file mode 100644 index 00000000..814760e4 --- /dev/null +++ b/docs/reference/privacy.md @@ -0,0 +1,28 @@ +# Privacy and diagnostic data + +The coding product sends user-approved prompts/code/tool results to the Cortex +coding service as described by the product. The local diagnostics added here +do **not** change that business traffic and do not add diagnostic destinations. + +| Data | Location | Handling | +| --- | --- | --- | +| Authentication | OS keyring or protected process environment | Never log, snapshot, commit, or attach | +| Sessions, messages, tool outputs | Existing session stores | May contain personal/customer data; minimize access and retention | +| Local diagnostic events | Explicit `CORTEX_DIAGNOSTICS_DIR` | Closed allowlist, no content or user IDs, bounded seven-day retention | +| CI test/coverage reports | Repository CI artifacts | Synthetic test data only; 14–30 day retention | +| CPU profiles / existing debug logs | Operator-selected local files | Potentially sensitive; no automatic upload | + +Consent is explicit: diagnostics are disabled unless the operator chooses an +output directory. Unset the variable to stop recording. Remove only that +operator-selected generated diagnostic directory to erase diagnostic history. +This does not erase session stores, backups, keyring credentials, or third-party +service data. Handle those separately through their existing lifecycle. + +For bug reports, use aggregate counts and a minimal synthetic reproduction. +Review any attachment manually. Existing `--debug` logs and session exports +are not made safe by this diagnostic allowlist. Do not feed them to external +analytics or automated issue creation. + +There is no claim of regulatory certification, centralized consent management, +or remote deletion guarantees in this repository. Retention/consent obligations +for deployment operators remain their responsibility. diff --git a/scripts/dev-setup.sh b/scripts/dev-setup.sh new file mode 100644 index 00000000..833b74c0 --- /dev/null +++ b/scripts/dev-setup.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/.." + +for tool in cargo rustc git python3 pkg-config; do + command -v "$tool" >/dev/null || { echo "Missing prerequisite: $tool. See docs/guides/development.md" >&2; exit 1; } +done +if [[ "$(uname -s)" == Linux ]]; then + pkg-config --exists alsa openssl || { + echo "Install libasound2-dev libssl-dev pkg-config (Debian/Ubuntu). See docs/guides/development.md" >&2 + exit 1 + } +fi +tools="$PWD/target/readiness-tools" +python3 -m venv "$tools" +"$tools/bin/python" -m pip install --disable-pip-version-check -r scripts/readiness/requirements.txt +cargo fetch --locked +cargo install --locked --version 0.9.1 cargo-machete --root "$tools" +cargo install --locked --version 0.9.102 cargo-nextest --root "$tools" +cargo install --locked --version 0.6.21 cargo-llvm-cov --root "$tools" +cargo install --locked --version 0.22.2 cargo-audit --root "$tools" +rustup component add llvm-tools-preview +echo "Setup complete. Use: export PATH=\"$tools/bin:\$PATH\"" diff --git a/scripts/readiness/coverage.py b/scripts/readiness/coverage.py new file mode 100644 index 00000000..2436828a --- /dev/null +++ b/scripts/readiness/coverage.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +"""Enforce 80% line coverage on changed executable application/shared lines.""" + +import argparse +import json +from pathlib import Path +import re + +from quality import ROOT, git + +# Gate production sources, not test harnesses or schema-export examples. +APPS = ("src/cortex-cli/src/", "src/cortex-app-server/src/", "src/cortex-common/src/") +MIN_PERCENT = 80 + +def parse_lcov(text): + files, current = {}, None + for line in text.splitlines(): + if line.startswith("SF:"): + path = Path(line[3:]) + current = str(path.relative_to(ROOT)) if path.is_absolute() else str(path) + files.setdefault(current, {}) + elif line.startswith("DA:") and current: + number, count, *_ = line[3:].split(",") + values = files[current] + values[int(number)] = values.get(int(number), 0) + int(count) + if not files: + raise ValueError("Coverage report contains no source files") + return files + +def changed_lines(diff): + result, path = {}, None + for line in diff.splitlines(): + if line.startswith("+++ b/"): + path = line[6:] + elif line.startswith("+++ "): + path = None + elif path and line.startswith("@@ "): + match = re.search(r"\+(\d+)(?:,(\d+))? @@", line) + if match: + start = int(match[1]) + count = int(match[2] or 1) + result.setdefault(path, set()).update(range(start, start + count)) + return result + +def evaluate(files, changes): + covered, total, missing, absent = 0, 0, [], [] + for path, lines in changes.items(): + if not path.startswith(APPS) or not path.endswith(".rs"): + continue + executable = files.get(path, {}) + if not executable and (ROOT / path).exists(): + text = (ROOT / path).read_text() + if re.search(r"\bfn\s+\w+", text): + absent.append(path) + for line in sorted(lines & executable.keys()): + total += 1 + if executable[line] > 0: + covered += 1 + else: + missing.append(f"{path}:{line}") + return { + "covered": covered, "executable_changed_lines": total, + "required_percent": MIN_PERCENT, "uncovered": missing, "absent_files": absent, + "passed": not absent and (not total or covered * 100 >= MIN_PERCENT * total), + } + +def run(base, report): + base = git("rev-parse", "--verify", f"{base}^{{commit}}").decode().strip() + changes = changed_lines(git("diff", "--no-ext-diff", "--unified=0", base, "--", *APPS).decode()) + for path in git("ls-files", "--others", "--exclude-standard").decode().splitlines(): + if path.startswith(APPS) and path.endswith(".rs"): + changes[path] = set(range(1, len((ROOT / path).read_text().splitlines()) + 1)) + result = evaluate(parse_lcov(report.read_text()), changes) + output = ROOT / "target/readiness/coverage.json" + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(json.dumps(result, indent=2) + "\n") + print(json.dumps(result, indent=2)) + return int(not result["passed"]) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--base", required=True) + parser.add_argument("--report", type=Path, default=ROOT / "target/readiness/lcov.info") + args = parser.parse_args() + raise SystemExit(run(args.base, args.report)) diff --git a/scripts/readiness/insights.py b/scripts/readiness/insights.py new file mode 100644 index 00000000..20314e4c --- /dev/null +++ b/scripts/readiness/insights.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Summarize allowlisted local events; never upload events or open issues.""" + +import argparse +from collections import Counter, defaultdict +import json +import math +from pathlib import Path +import re + +OPERATIONS = { + "cli.command", "cli.interactive", "cli.debug", "server.request", + "session.created", "session.deleted", "server.started", "health.check", +} +ERROR_PERCENT = 5 +P95_MILLISECONDS = 2000 +MIN_REQUESTS = 20 + +def summarize(events): + versions = defaultdict(lambda: {"operations": Counter(), "requests": 0, "errors": 0, "durations": []}) + for event in events: + if event.get("schema") != 1 or event.get("operation") not in OPERATIONS: + raise ValueError("Unknown local diagnostic event schema or operation") + version = event.get("version", "") + if not isinstance(version, str) or not re.fullmatch(r"\d+\.\d+\.\d+", version): + raise ValueError("Invalid application version") + status, duration = event.get("status"), event.get("duration_ms") + if type(status) is not int or not 100 <= status <= 599 or type(duration) is not int or duration < 0: + raise ValueError("Invalid numeric diagnostic fields") + group = versions[version] + group["operations"][event["operation"]] += 1 + if event["operation"] in {"server.request", "cli.command", "cli.debug", "cli.interactive"}: + group["requests"] += 1 + group["errors"] += status >= 500 + if event["operation"] == "server.request": + group["durations"].append(duration) + result = {} + for version, group in sorted(versions.items()): + durations = sorted(group.pop("durations")) + p95 = durations[math.ceil(len(durations) * .95) - 1] if durations else None + group["p95_request_ms"] = p95 + group["alerts"] = [] + if group["requests"] >= MIN_REQUESTS and group["errors"] * 100 >= ERROR_PERCENT * group["requests"]: + group["alerts"].append("error_rate") + if len(durations) >= MIN_REQUESTS and p95 > P95_MILLISECONDS: + group["alerts"].append("request_latency") + result[version] = group + return result + +def load(directory): + events = [] + for path in sorted(directory.glob("run-*.jsonl")): + if path.is_symlink() or not path.is_file() or path.stat().st_size > 2 * 1024 * 1024: + raise ValueError("Unsafe or oversized local diagnostics file") + for line in path.read_text().splitlines(): + events.append(json.loads(line)) + if not events: + raise ValueError("No local events found, cannot claim a healthy deployment") + return events + +def run(directory): + report = summarize(load(directory)) + print(json.dumps({ + "local_only": True, + "thresholds": {"minimum_samples": MIN_REQUESTS, "error_percent": ERROR_PERCENT, "p95_request_ms": P95_MILLISECONDS}, + "versions": report, + "next_step": "For alerts, follow docs/guides/operations.md. Reproduce with local QA before drafting a redacted issue. Never attach raw sessions or logs.", + }, indent=2)) + return int(any(group["alerts"] for group in report.values())) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("directory", type=Path) + raise SystemExit(run(parser.parse_args().directory)) diff --git a/scripts/readiness/qa.py b/scripts/readiness/qa.py new file mode 100644 index 00000000..0b67023f --- /dev/null +++ b/scripts/readiness/qa.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +"""Exercise the built CLI and a real isolated loopback server, including DAST.""" + +import argparse +import json +import os +from pathlib import Path +import secrets +import socket +import subprocess +import tempfile +import time +from urllib.error import HTTPError, URLError +from urllib.request import HTTPRedirectHandler, ProxyHandler, Request, build_opener + +ROOT = Path(__file__).resolve().parents[2] + +class NoRedirect(HTTPRedirectHandler): + def redirect_request(self, req, fp, code, msg, headers, newurl): + return None + +def check(condition, message): + if not condition: + raise AssertionError(message) + +def cli_flow(binary, env, home): + command = [str(binary), "debug", "doctor", "--json"] + output = subprocess.run(command, cwd=home, env=env, capture_output=True, text=True, timeout=20) + check(output.returncode == 0, "CLI local readiness failed") + report = json.loads(output.stdout) + check(report["ready"] is True and report["coding_service"] == "not_checked", "CLI misreported local scope") + (home / "config.toml").write_text("invalid = [") + output = subprocess.run(command, cwd=home, env=env, capture_output=True, text=True, timeout=20) + check(output.returncode != 0, "CLI accepted invalid configuration") + check(json.loads(output.stdout)["checks"]["configuration"] is False, "CLI missed the configuration error") + (home / "config.toml").unlink() + return ["cli.local_readiness", "cli.invalid_configuration"] + +def server_flow(binary, env, workspace, output): + with socket.socket() as reservation: + reservation.bind(("127.0.0.1", 0)) + port = reservation.getsockname()[1] + key = secrets.token_urlsafe(32) + env = {**env, "CORTEX_SERVER_API_KEY": key} + config = workspace / "server.json" + config.write_text(json.dumps({ + "listen_addr": f"127.0.0.1:{port}", + "max_body_size": 4096, + "rate_limit": {"burst_size": 100}, + })) + opener = build_opener(ProxyHandler({}), NoRedirect()) + base = f"http://127.0.0.1:{port}/api/v1" + + def call(method, path, body=None, authenticated=True, headers=None): + values = {"Content-Type": "application/json"} + if authenticated: + values["Authorization"] = f"ApiKey {key}" + values.update(headers or {}) + request = Request(base + path, method=method, headers=values, + data=json.dumps(body).encode() if body is not None else None) + try: + response = opener.open(request, timeout=5) + except HTTPError as error: + response = error + with response: + data = response.read(1024 * 1024) + headers = {key.lower(): value for key, value in response.headers.items()} + body = json.loads(data) if data and headers.get("content-type", "").startswith("application/json") else None + return response.status, headers, body + + with (output / "server.log").open("w") as log: + process = subprocess.Popen([str(binary), "--config", str(config), "--json-logs"], + env=env, cwd=workspace, stdout=log, stderr=log) + try: + deadline = time.monotonic() + 20 + while True: + check(process.poll() is None, "Local server exited before becoming ready") + try: + status, _, body = call("GET", "/health", authenticated=False) + if status == 200: + check(body["status"] == "ready", "Server did not report local readiness") + break + except (URLError, ConnectionError, TimeoutError): + pass + check(time.monotonic() < deadline, "Local server readiness timed out") + time.sleep(.1) + for path in ["/sessions", "/metrics", "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/admin/stats", "/ws", "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/health/sessions"]: + check(call("GET", path, authenticated=False)[0] == 401, "Authentication boundary failed") + check(call("GET", "/sessions", headers={"Authorization": "ApiKey invalid-fixture"})[0] == 401, "Invalid key was accepted") + status, headers, session = call("POST", "/sessions", {"model": "local-qa"}) + check(status == 200, "Session creation failed") + check(any(name.lower() == "x-request-id" for name in headers), "Missing request correlation") + check(any(name.lower() == "traceparent" for name in headers), "Missing local trace context") + path = f"/sessions/{session['id']}" + check(call("POST", path + "/messages", {"content": "local QA fixture"})[0] == 200, "Message storage failed") + check(call("GET", path + "/messages")[2][0]["content"] == "local QA fixture", "Stored message changed") + check(call("GET", path)[2]["message_count"] == 1, "Session count did not update") + check(call("DELETE", path)[2]["deleted"] is True, "Session deletion failed") + check(call("GET", path)[0] == 404, "Deleted session remains visible") + check(call("POST", "/sessions", {"model": "x" * 8192})[0] == 413, "Body size limit is not enforced") + check(call("GET", "/sessions", headers={"Origin": "https://untrusted.example"})[1].get("access-control-allow-origin") is None, "CORS allowed an unknown origin") + check(call("POST", "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/files/read", {"path": "../outside-fixture.txt"})[0] == 403, "Read escaped the workspace") + check(call("POST", "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/files/write", {"path": "../new/outside.txt", "content": "fixture"})[0] == 400, "Write escaped the workspace") + if os.name == "posix": + (workspace / "escape").symlink_to(workspace.parent, target_is_directory=True) + check(call("POST", "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/files/read", {"path": "escape/outside-fixture.txt"})[0] == 403, "Symlink escaped the workspace") + check(call("POST", "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/files/mkdir", {"path": "fixture-dir"})[0] == 200, "Directory creation failed") + check(call("POST", "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/files/write", {"path": "fixture-dir/file", "content": "file fixture"})[0] == 200, "File write failed") + check(call("POST", "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/files/read", {"path": "fixture-dir/file"})[2]["content"] == "file fixture", "File readback failed") + check(call("POST", "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/files/mkdir", {"path": "../forbidden-dir"})[0] == 400, "Directory creation escaped the workspace") + for source, target in [ + ("../outside-fixture.txt", "fixture-dir/stolen"), + ("fixture-dir/file", "../stolen"), + (".", "../moved-workspace"), + ]: + check(call("POST", "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/files/rename", {"old_path": source, "new_path": target})[0] == 400, "Rename escaped or moved the workspace") + check(call("POST", "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/files/rename", {"old_path": "fixture-dir/file", "new_path": "fixture-dir/renamed"})[0] == 200, "Local rename failed") + check(call("POST", "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/files/delete", {"path": "fixture-dir/renamed"})[0] == 200, "Local deletion failed") + check((workspace.parent / "outside-fixture.txt").exists(), "Security checks modified outside data") + check(not (workspace.parent / "forbidden-dir").exists(), "Security checks created an outside directory") + metrics = call("GET", "/metrics")[2] + check(metrics["total_requests"] >= 15 and metrics["sessions_created"] == 1, "Metrics were not wired to live requests") + check(call("GET", "/openapi.json")[2]["openapi"] == "3.1.0", "API schema unavailable") + finally: + process.terminate() + try: + process.wait(timeout=10) + except subprocess.TimeoutExpired: + process.kill() + process.wait() + return [ + "server.local_readiness", "server.authentication", "server.session_crud", + "server.message_storage", "server.correlation", "server.metrics", + "dast.body_limit", "dast.cors", "dast.workspace_traversal", "dast.symlink_escape", + "server.file_crud", "dast.file_mutations", + ] + +def run(bin_dir): + output = ROOT / "target/readiness/qa" + output.mkdir(parents=True, exist_ok=True) + cases = [] + with tempfile.TemporaryDirectory(prefix="cortex-local-qa-") as temporary: + root = Path(temporary) + home, workspace = root / "home", root / "workspace" + home.mkdir() + workspace.mkdir() + (root / "outside-fixture.txt").write_text("private QA fixture") + # Do not inherit credentials, proxies, user configuration, or mDNS settings. + env = {key: os.environ[key] for key in ("PATH", "SYSTEMROOT", "WINDIR") if key in os.environ} + env.update({ + "HOME": str(home), "CORTEX_HOME": str(home), "NO_COLOR": "1", + "CORTEX_MDNS_ENABLED": "false", + "CORTEX_DIAGNOSTICS_DIR": str(root / "diagnostics"), + }) + try: + cases.extend(cli_flow(bin_dir / "Cortex", env, home)) + cases.extend(server_flow(bin_dir / "cortex-server", env, workspace, output)) + except Exception: + (output / "report.json").write_text(json.dumps({"passed": False, "completed_cases": cases}, indent=2) + "\n") + raise + # Validate the generated journal, but retain only aggregated, allowlisted data. + from insights import load, summarize + insights = summarize(load(root / "diagnostics")) + (output / "local-insights.json").write_text(json.dumps(insights, indent=2) + "\n") + (output / "report.json").write_text(json.dumps({"passed": True, "completed_cases": cases}, indent=2) + "\n") + print(f"Passed {len(cases)} local functional/security cases. No model turn was simulated.") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--bin-dir", type=Path, default=ROOT / "target/debug") + run(parser.parse_args().bin_dir.resolve()) diff --git a/scripts/readiness/quality.py b/scripts/readiness/quality.py new file mode 100644 index 00000000..e32259d9 --- /dev/null +++ b/scripts/readiness/quality.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python3 +"""Source-quality reports with a no-new-debt gate against a Git base.""" + +import argparse +from collections import Counter +import hashlib +import json +from pathlib import Path +import re +import subprocess +import sys +import tomllib + +ROOT = Path(__file__).resolve().parents[2] +MAX_BYTES = 5 * 1024 * 1024 +MAX_LINES = 1000 +MAX_COMPLEXITY = 25 +MIN_CLONE_TOKENS = 100 +FLAG_REGISTRIES = { + "src/cortex-experimental/src/registry.rs", + "src/cortex-engine/src/features.rs", +} + + +def git(*args): + return subprocess.check_output(["git", "-C", str(ROOT), *args]) + + +def source_paths(): + paths = git("ls-files", "--cached", "--others", "--exclude-standard", "-z") + return sorted({ + p.decode() for p in paths.split(b"\0") + if p and not p.startswith(b".git/") and (ROOT / p.decode()).is_file() + }) + + +def rust_metrics(path, text): + import lizard + from lizard_languages.rust import RustReader + + result = {} + occurrences = Counter() + lines = text.splitlines(keepends=True) + for function in lizard.analyze_file.analyze_source_code(path, text).function_list: + # Trait implementations can have identical signatures in the same file. + # Keep every occurrence rather than silently overwriting a measurement. + occurrences[function.long_name] += 1 + key = f"{path}:{function.long_name}#{occurrences[function.long_name]}" + tokens = [ + t for t in RustReader.generate_tokens( + "".join(lines[function.start_line - 1:function.end_line]) + ) + if t.strip() and not t.startswith(("//", "/*")) + ] + # Compare complete bodies, not signatures or common short boilerplate. + body = tokens[tokens.index("{"):] if "{" in tokens else [] + fingerprint = None + if len(body) >= MIN_CLONE_TOKENS: + fingerprint = hashlib.sha256( + json.dumps(body, separators=(",", ":")).encode() + ).hexdigest() + result[key] = { + "path": path, + "line": function.start_line, + "complexity": function.cyclomatic_complexity, + "clone": fingerprint, + } + return result + + +def clones(metrics): + groups = {} + for key, item in metrics.items(): + if item["clone"]: + groups.setdefault(item["clone"], set()).add(key) + return {key: members for key, members in groups.items() if len(members) > 1} + + +def quality_findings(current, previous): + findings = [] + for key, item in current.items(): + old = previous.get(key, {}).get("complexity", MAX_COMPLEXITY) + if item["complexity"] > MAX_COMPLEXITY: + findings.append({ + "kind": "complexity", "path": item["path"], "line": item["line"], + "message": f"{key}: complexity {item['complexity']} (target {MAX_COMPLEXITY})", + "regression": item["complexity"] > max(MAX_COMPLEXITY, old), + }) + old_clones = clones(previous) + for fingerprint, members in clones(current).items(): + added = members - old_clones.get(fingerprint, set()) + for key in sorted(members): + item = current[key] + findings.append({ + "kind": "duplication", "path": item["path"], "line": item["line"], + "message": f"Duplicate function body: {key}; also {', '.join(sorted(members - {key}))}", + "regression": key in added, + }) + return findings + +def flag_inventory(sources): + """Conservative literal-use analysis; dynamic registrations need review.""" + from lizard_languages.rust import RustReader + + definitions, consumers = {}, set() + for path, text in sources.items(): + # Built-in registries and their consumers put test modules at the end. + # Test-only references must not keep a retired production flag alive. + production = re.split(r"#\s*\[\s*cfg\s*\(\s*test\s*\)\s*\]", text, maxsplit=1)[0] + tokens = " ".join( + t for t in RustReader.generate_tokens(production) + if t.strip() and not t.startswith(("//", "/*")) + ) + if path in FLAG_REGISTRIES: + for name in re.findall(r'Feature\s*::\s*new\s*\(\s*"([^"]+)"', tokens): + definitions[f"{path}:{name}"] = (path, name) + else: + consumers.update(re.findall( + r'(?:is_enabled|is_feature_enabled)\s*\(\s*"([^"]+)"', tokens + )) + return { + key: {"path": path, "name": name} + for key, (path, name) in definitions.items() if name not in consumers + } + +def flag_findings(current, previous): + old = flag_inventory(previous) + return [ + { + "kind": "unused-feature-flag", "path": item["path"], "line": 1, + "message": f"{item['name']}: no production literal consumer; remove it or document and test its dynamic consumer", + "regression": key not in old, + } + for key, item in flag_inventory(current).items() + ] + + +def dependency_tables(manifest): + for section in ("dependencies", "dev-dependencies", "build-dependencies"): + yield section, manifest.get(section, {}) + for target, manifest in manifest.get("target", {}).items(): + for section in ("dependencies", "dev-dependencies", "build-dependencies"): + yield f"target.{target}.{section}", manifest.get(section, {}) + + +def dependency_findings(root_manifest, manifests, exceptions): + findings = [] + shared = root_manifest["workspace"]["dependencies"] + used = set() + for path, manifest in manifests.items(): + for section, dependencies in dependency_tables(manifest): + for name, declaration in dependencies.items(): + if isinstance(declaration, dict) and declaration.get("workspace") is True: + continue + package = declaration.get("package", name) if isinstance(declaration, dict) else name + if package not in shared: + continue + # Renamed internal crates retain their alias in consumers. Their + # package version is already inherited from this workspace. + canonical = shared[package] + if isinstance(declaration, dict) and isinstance(canonical, dict): + local = declaration.get("path") + shared_path = canonical.get("path") + if local and shared_path and "version" not in declaration: + if (ROOT / path).parent.joinpath(local).resolve() == (ROOT / shared_path).resolve(): + continue + key = f"{path}:{section}:{name}" + exception = exceptions.get(key) + if exception and exception.get("declaration") == declaration and exception.get("reason"): + used.add(key) + continue + findings.append(f"{key}: inherit the workspace dependency or document an exact compatibility constraint") + for key in exceptions.keys() - used: + findings.append(f"{key}: unused or stale dependency exception") + return findings + + +def check_dependencies(): + root = tomllib.loads((ROOT / "Cargo.toml").read_text()) + manifests = { + f"{member}/Cargo.toml": tomllib.loads((ROOT / member / "Cargo.toml").read_text()) + for member in root["workspace"]["members"] + } + path = ROOT / ".quality/dependency-compatibility.json" + exceptions = json.loads(path.read_text()) if path.exists() else {} + return dependency_findings(root, manifests, exceptions) + + +def local_links(text): + return re.findall(r"\[[^\]]*\]\(([^)\s]+)(?:\s+\"[^\"]*\")?\)", text) + + +def check_agents(): + path = ROOT / "AGENTS.md" + failures = [] + for link in local_links(path.read_text()): + if "://" in link or link.startswith("#"): + continue + target = (path.parent / link.split("#", 1)[0]).resolve() + if not target.is_relative_to(ROOT) or not target.exists(): + failures.append(f"AGENTS.md: broken or out-of-repository link: {link}") + for script in re.findall(r"\./(scripts/[\w/.-]+)", path.read_text()): + if not (ROOT / script).is_file(): + failures.append(f"AGENTS.md: missing command {script}") + return failures + + +def annotation(level, path, line, message): + # GitHub workflow-command escaping, including filenames controlled by a PR. + def escape(text): + return str(text).replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A").replace(",", "%2C").replace(":", "%3A") + print(f"::{level} file={escape(path)},line={line}::{escape(message)}") + + +def run(base, output): + base = git("rev-parse", "--verify", f"{base}^{{commit}}").decode().strip() + previous_paths = set(git("ls-tree", "-r", "--name-only", base).decode().splitlines()) + metrics, previous, findings = {}, {}, [] + sources, old_sources = {}, {} + for path in source_paths(): + data = (ROOT / path).read_bytes() + before = git("show", f"{base}:{path}") if path in previous_paths else b"" + if len(data) > MAX_BYTES: + findings.append({ + "kind": "file-size", "path": path, "line": 1, + "message": f"File is {len(data)} bytes (limit {MAX_BYTES})", + "regression": True, + }) + if not path.endswith(".rs"): + continue + line_count = len(data.splitlines()) + if line_count > MAX_LINES: + findings.append({ + "kind": "file-lines", "path": path, "line": 1, + "message": f"Rust file has {line_count} lines (target {MAX_LINES})", + "regression": line_count > max(MAX_LINES, len(before.splitlines())), + }) + sources[path] = data.decode() + metrics.update(rust_metrics(path, sources[path])) + if before: + old_sources[path] = before.decode() + previous.update(rust_metrics(path, old_sources[path])) + for path in sorted(previous_paths - sources.keys()): + if path.endswith(".rs"): + old_sources[path] = git("show", f"{base}:{path}").decode() + previous.update(rust_metrics(path, old_sources[path])) + findings.extend(quality_findings(metrics, previous)) + findings.extend(flag_findings(sources, old_sources)) + failures = check_dependencies() + check_agents() + output.mkdir(parents=True, exist_ok=True) + report = { + "base": base, "functions_analyzed": len(metrics), + "thresholds": {"complexity": MAX_COMPLEXITY, "source_lines": MAX_LINES, "file_bytes": MAX_BYTES, "clone_tokens": MIN_CLONE_TOKENS}, + "findings": findings, "policy_failures": failures, + } + (output / "quality.json").write_text(json.dumps(report, indent=2) + "\n") + for finding in findings: + if finding["regression"]: + annotation("error", finding["path"], finding["line"], finding["message"]) + for failure in failures: + annotation("error", "Cargo.toml" if "dependency" in failure else "AGENTS.md", 1, failure) + regressions = sum(f["regression"] for f in findings) + debt = len(findings) - regressions + print(f"Analyzed {len(metrics)} Rust functions: {regressions} regressions, {len(failures)} policy failures, {debt} existing findings (see quality.json).") + return int(bool(regressions or failures)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--base", default="origin/main", help="Git baseline; CI must supply the PR base commit") + parser.add_argument("--output", type=Path, default=ROOT / "target/readiness") + args = parser.parse_args() + sys.exit(run(args.base, args.output)) diff --git a/scripts/readiness/release_age.py b/scripts/readiness/release_age.py new file mode 100644 index 00000000..cc973650 --- /dev/null +++ b/scripts/readiness/release_age.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""Reject newly locked crates.io releases younger than seven days.""" + +import argparse +from datetime import datetime, timedelta, timezone +import json +from pathlib import Path +import subprocess +import time +import tomllib +from urllib.request import Request, urlopen + +ROOT = Path(__file__).resolve().parents[2] +MIN_AGE = timedelta(days=7) + +def registry_packages(lock): + return { + (p["name"], p["version"]) for p in lock["package"] + if p.get("source") == "registry+https://github.com/rust-lang/crates.io-index" + } + +def old_enough(created_at, now): + created = datetime.fromisoformat(created_at.replace("Z", "+00:00")) + if created.tzinfo is None: + raise ValueError("Registry timestamp is missing its timezone") + return now - created >= MIN_AGE + +def run(base): + base = subprocess.check_output(["git", "-C", str(ROOT), "rev-parse", "--verify", f"{base}^{{commit}}"], text=True).strip() + previous = subprocess.check_output(["git", "-C", str(ROOT), "show", f"{base}:Cargo.lock"], text=True) + added = registry_packages(tomllib.loads((ROOT / "Cargo.lock").read_text())) - registry_packages(tomllib.loads(previous)) + now = datetime.now(timezone.utc) + failures = [] + for name, version in sorted(added): + request = Request( + f"https://crates.io/api/v1/crates/{name}/{version}", + headers={"User-Agent": "CortexLM-cli-dependency-policy (github.com/CortexLM/cli)"}, + ) + # Fail closed when registry evidence is unavailable. Never substitute now. + with urlopen(request, timeout=30) as response: + release = json.load(response)["version"] + if release["yanked"] or not old_enough(release["created_at"], now): + failures.append(f"{name}@{version}: yanked or younger than seven days") + time.sleep(1) + print("\n".join(failures) or f"Release-age policy passed for {len(added)} newly locked releases") + return int(bool(failures)) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--base", required=True) + raise SystemExit(run(parser.parse_args().base)) diff --git a/scripts/readiness/requirements.txt b/scripts/readiness/requirements.txt new file mode 100644 index 00000000..6b26afcf --- /dev/null +++ b/scripts/readiness/requirements.txt @@ -0,0 +1,4 @@ +lizard==1.17.31 +pathspec==1.1.1 +Pygments==2.21.0 +PyYAML==6.0.2 diff --git a/scripts/readiness/schema.py b/scripts/readiness/schema.py new file mode 100644 index 00000000..844a448a --- /dev/null +++ b/scripts/readiness/schema.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +"""Regenerate or verify the checked-in schema from the actual Rust API models.""" + +import argparse +from pathlib import Path +import subprocess + +ROOT = Path(__file__).resolve().parents[2] +SCHEMAS = [ + ("cortex-app-server", "export-schema", "app-server.openapi.json"), + ("cortex-cli", "export-cli-schema", "cli.commands.json"), +] + +def run(write): + for package, example, filename in SCHEMAS: + content = subprocess.check_output([ + "cargo", "run", "--locked", "--quiet", "-p", package, + "--example", example, + ], cwd=ROOT, text=True) + schema = ROOT / "docs/reference" / filename + if write: + schema.parent.mkdir(parents=True, exist_ok=True) + schema.write_text(content) + elif not schema.exists() or schema.read_text() != content: + raise SystemExit("API schema is stale: run python3 scripts/readiness/schema.py --write") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--write", action="store_true") + run(parser.parse_args().write) diff --git a/scripts/readiness/test_coverage.py b/scripts/readiness/test_coverage.py new file mode 100644 index 00000000..4f6d33d0 --- /dev/null +++ b/scripts/readiness/test_coverage.py @@ -0,0 +1,42 @@ +import unittest +from pathlib import Path +from tempfile import TemporaryDirectory +from unittest.mock import patch + +from coverage import changed_lines, evaluate, parse_lcov + +class CoverageTests(unittest.TestCase): + def test_changed_lines_not_deleted_lines(self): + diff = "+++ b/src/cortex-cli/a.rs\n@@ -1 +1,2 @@\n@@ -8,2 +9,0 @@" + self.assertEqual(changed_lines(diff), {"src/cortex-cli/a.rs": {1, 2}}) + + def test_real_threshold_cannot_round_up(self): + path = "src/cortex-cli/src/example.rs" + lines = {i: int(i < 8) for i in range(10)} + self.assertTrue(evaluate({path: lines}, {path: set(lines)})["passed"]) + lines[7] = 0 + self.assertFalse(evaluate({path: lines}, {path: set(lines)})["passed"]) + + def test_lcov_merges_counts_and_rejects_empty_data(self): + path = "src/cortex-cli/example.rs" + report = f"SF:{path}\nDA:1,0\nend_of_record\nSF:{path}\nDA:1,2\n" + self.assertEqual(parse_lcov(report)[path][1], 2) + with self.assertRaises(ValueError): + parse_lcov("") + + def test_missing_production_coverage_fails_but_harnesses_are_not_gated(self): + paths = [ + "src/cortex-cli/src/missing.rs", + "src/cortex-cli/tests/check.rs", + "src/cortex-app-server/examples/export-schema.rs", + ] + with TemporaryDirectory() as directory: + root = Path(directory) + for path in paths: + source = root / path + source.parent.mkdir(parents=True, exist_ok=True) + source.write_text("fn uncovered() {}\n") + with patch("coverage.ROOT", root): + result = evaluate({}, {path: {1} for path in paths}) + self.assertFalse(result["passed"]) + self.assertEqual(result["absent_files"], paths[:1]) diff --git a/scripts/readiness/test_insights.py b/scripts/readiness/test_insights.py new file mode 100644 index 00000000..59e8a892 --- /dev/null +++ b/scripts/readiness/test_insights.py @@ -0,0 +1,25 @@ +import unittest + +from insights import summarize + +def event(**values): + return {"schema":1, "version":"0.1.7", "operation":"server.request", "status":200, "duration_ms":10, **values} + +class InsightTests(unittest.TestCase): + def test_error_and_latency_alerts_require_real_samples(self): + events = [event() for _ in range(19)] + events.append(event(status=500)) + self.assertEqual(summarize(events)["0.1.7"]["alerts"], ["error_rate"]) + self.assertEqual(summarize(events[:19])["0.1.7"]["alerts"], []) + self.assertIn("request_latency", summarize([event(duration_ms=3000) for _ in range(20)])["0.1.7"]["alerts"]) + + def test_versions_remain_separate_and_private_fields_are_omitted(self): + result = summarize([event(prompt="private"), event(version="0.1.8", status=500)]) + self.assertEqual(result["0.1.7"]["errors"], 0) + self.assertEqual(result["0.1.8"]["errors"], 1) + self.assertNotIn("private", str(result)) + + def test_unknown_events_cannot_disclose_arbitrary_text(self): + for bad in [event(operation="private"), event(version="private"), event(status="private")]: + with self.assertRaises(ValueError): + summarize([bad]) diff --git a/scripts/readiness/test_quality.py b/scripts/readiness/test_quality.py new file mode 100644 index 00000000..bdead32d --- /dev/null +++ b/scripts/readiness/test_quality.py @@ -0,0 +1,95 @@ +import unittest + +from quality import check_agents, dependency_findings, flag_findings, quality_findings, rust_metrics + + +class QualityTests(unittest.TestCase): + def test_rust_complexity_counts_branches(self): + metrics = rust_metrics("sample.rs", "fn choose(a: bool, b: bool) { if a && b { run(); } }") + self.assertEqual(len(metrics), 1) + self.assertGreaterEqual(next(iter(metrics.values()))["complexity"], 3) + + def test_new_complexity_fails_but_reduction_does_not(self): + item = {"path": "a.rs", "line": 1, "complexity": 30, "clone": None} + current = {"a.rs:run()": item} + self.assertTrue(quality_findings(current, {})[0]["regression"]) + previous = {"a.rs:run()": dict(item, complexity=31)} + self.assertFalse(quality_findings(current, previous)[0]["regression"]) + previous["a.rs:run()"]["complexity"] = 29 + self.assertTrue(quality_findings(current, previous)[0]["regression"]) + + def test_new_duplicate_is_detected(self): + body = " ".join(f"work({i});" for i in range(40)) + a = rust_metrics("a.rs", f"fn first() {{ {body} }}") + b = rust_metrics("b.rs", f"fn second() {{ {body} }}") + findings = quality_findings(a | b, a) + self.assertEqual(len([f for f in findings if f["kind"] == "duplication"]), 2) + self.assertTrue(all(f["regression"] for f in findings)) + self.assertFalse(any(f["regression"] for f in quality_findings(a | b, a | b))) + + def test_small_boilerplate_is_not_a_clone(self): + a = rust_metrics("a.rs", "fn first() { true }") + b = rust_metrics("b.rs", "fn second() { true }") + self.assertFalse(quality_findings(a | b, {})) + + def test_repeated_trait_method_signatures_are_not_lost(self): + metrics = rust_metrics("a.rs", """ +impl A { fn run(&self) { first(); } } +impl B { fn run(&self) { if condition() { second(); } } } +""") + self.assertEqual(len(metrics), 2) + self.assertEqual(sorted(f["complexity"] for f in metrics.values()), [1, 2]) + + def test_renamed_dependencies_cannot_bypass_version_policy(self): + root = {"workspace": {"dependencies": {"serde": "1"}}} + manifests = {"a": {"dependencies": {"serde_old": {"package": "serde", "version": "0.9"}}}} + self.assertTrue(dependency_findings(root, manifests, {})) + + def test_renamed_internal_crate_must_point_to_workspace_package(self): + root = {"workspace": {"dependencies": {"shared": {"path": "src/shared"}}}} + manifests = {"src/app/Cargo.toml": {"dependencies": {"shared_ext": {"package": "shared", "path": "../shared"}}}} + self.assertEqual(dependency_findings(root, manifests, {}), []) + manifests["src/app/Cargo.toml"]["dependencies"]["shared_ext"]["path"] = "../other" + self.assertTrue(dependency_findings(root, manifests, {})) + + def test_dependency_overrides_are_rejected(self): + root = {"workspace": {"dependencies": {"serde": "1"}}} + self.assertTrue(dependency_findings(root, {"a": {"dependencies": {"serde": "2"}}}, {})) + self.assertFalse(dependency_findings(root, {"a": {"dependencies": {"serde": {"workspace": True}}}}, {})) + + def test_exact_compatibility_exception_cannot_hide_new_drift(self): + root = {"workspace": {"dependencies": {"rand": "0.9"}}} + manifests = {"a": {"dependencies": {"rand": "0.8"}}} + exception = {"a:dependencies:rand": {"declaration": "0.8", "reason": "Older RNG API"}} + self.assertFalse(dependency_findings(root, manifests, exception)) + manifests["a"]["dependencies"]["rand"] = "0.7" + self.assertTrue(dependency_findings(root, manifests, exception)) + self.assertTrue(dependency_findings(root, {}, exception)) + + def test_target_dependencies_are_checked(self): + root = {"workspace": {"dependencies": {"libc": "0.2"}}} + manifests = {"a": {"target": {"cfg(unix)": {"dependencies": {"libc": "0.1"}}}}} + self.assertTrue(dependency_findings(root, manifests, {})) + + def test_agents_links_and_scripts_resolve(self): + self.assertEqual(check_agents(), []) + + def test_flags_need_production_consumers_not_comments_or_tests(self): + registry = "src/cortex-experimental/src/registry.rs" + sources = { + registry: 'Feature::new("retired", "Name", "Description");', + "consumer.rs": '// flags.is_enabled("retired")\n#[cfg(test)] mod tests { flags.is_enabled("retired"); }', + } + self.assertTrue(flag_findings(sources, {})[0]["regression"]) + self.assertFalse(flag_findings(sources, sources)[0]["regression"]) + sources["consumer.rs"] = 'flags.is_enabled("retired");' + self.assertEqual(flag_findings(sources, {}), []) + + def test_removing_the_last_flag_consumer_fails(self): + registry = "src/cortex-experimental/src/registry.rs" + before = {registry: 'Feature::new("flag", "Name", "Description");', "a.rs": 'flags.is_enabled("flag");'} + self.assertTrue(flag_findings({registry: before[registry]}, before)[0]["regression"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/readiness/test_release_age.py b/scripts/readiness/test_release_age.py new file mode 100644 index 00000000..72fb65d0 --- /dev/null +++ b/scripts/readiness/test_release_age.py @@ -0,0 +1,20 @@ +from datetime import datetime, timedelta, timezone +import unittest + +from release_age import old_enough, registry_packages + +class ReleaseAgeTests(unittest.TestCase): + def test_boundary_and_future_releases(self): + now = datetime(2026, 1, 20, tzinfo=timezone.utc) + self.assertTrue(old_enough((now - timedelta(days=7)).isoformat(), now)) + self.assertFalse(old_enough((now - timedelta(days=6)).isoformat(), now)) + self.assertFalse(old_enough((now + timedelta(days=1)).isoformat(), now)) + with self.assertRaises(ValueError): + old_enough("2026-01-01T00:00:00", now) + + def test_only_crates_io_releases_are_queried(self): + lock = {"package": [ + {"name": "local", "version": "1"}, + {"name": "crate", "version": "2", "source": "registry+https://github.com/rust-lang/crates.io-index"}, + ]} + self.assertEqual(registry_packages(lock), {("crate", "2")}) diff --git a/scripts/readiness/test_reports.py b/scripts/readiness/test_reports.py new file mode 100644 index 00000000..3ccb0dc7 --- /dev/null +++ b/scripts/readiness/test_reports.py @@ -0,0 +1,27 @@ +from pathlib import Path +import tempfile +import unittest + +from tests import parse_junit, summarize + +class ReportTests(unittest.TestCase): + def test_failed_then_passed_is_flaky_and_fails(self): + runs = [ + {"exit_code": code, "tests": [{"name": "case", "status": status}]} + for code, status in [(100, "fail"), (0, "pass")] + ] + result = summarize(runs) + self.assertEqual(result["flaky_tests"], ["case"]) + self.assertTrue(result["failed"]) + + def test_build_failure_without_tests_fails(self): + self.assertTrue(summarize([{"exit_code": 1, "tests": []}])["failed"]) + + def test_junit_timings_and_failure_are_preserved(self): + with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / "junit.xml" + path.write_text('') + self.assertEqual(parse_junit(path), [{"name": "app::case", "status": "fail", "seconds": 1.5}]) + path.write_text("") + with self.assertRaises(ValueError): + parse_junit(path) diff --git a/scripts/readiness/test_workflows.py b/scripts/readiness/test_workflows.py new file mode 100644 index 00000000..0146ca09 --- /dev/null +++ b/scripts/readiness/test_workflows.py @@ -0,0 +1,58 @@ +import shlex +import tomllib +import unittest +from pathlib import Path + +import yaml + +ROOT = Path(__file__).resolve().parents[2] + + +class WorkflowTests(unittest.TestCase): + def setUp(self): + self.ci = yaml.safe_load((ROOT / ".github/workflows/ci.yml").read_text()) + + def test_policy_job_installs_required_toolchain_components(self): + toolchain = tomllib.loads((ROOT / "rust-toolchain.toml").read_text())["toolchain"] + step = next( + step for step in self.ci["jobs"]["quality"]["steps"] + if step.get("uses", "").startswith("dtolnay/rust-toolchain@") + ) + self.assertEqual(step["uses"], f"dtolnay/rust-toolchain@{toolchain['channel']}") + components = {value.strip() for value in step["with"]["components"].split(",")} + self.assertTrue(set(toolchain["components"]) <= components) + + def test_doctor_jobs_install_real_tools_before_tests(self): + stability = yaml.safe_load( + (ROOT / ".github/workflows/test-stability.yml").read_text() + ) + jobs = [ + self.ci["jobs"]["test"], + self.ci["jobs"]["coverage"], + stability["jobs"]["repeat"], + ] + for job in jobs: + installed = set() + found_tests = False + for step in job["steps"]: + command = step.get("run", "") + for line in command.splitlines(): + if "apt-get install" in line: + installed.update(shlex.split(line)) + if "scripts/readiness/tests.py" in command or "cargo llvm-cov nextest" in command: + self.assertTrue({"git", "ripgrep"} <= installed) + found_tests = True + self.assertTrue(found_tests) + + def test_coverage_creates_report_directory_before_export(self): + commands = "\n".join( + step.get("run", "") for step in self.ci["jobs"]["coverage"]["steps"] + ) + lines = [line.strip() for line in commands.splitlines()] + directory = lines.index("mkdir -p target/readiness") + report = next( + index for index, line in enumerate(lines) + if "cargo llvm-cov nextest" in line + and "--output-path target/readiness/lcov.info" in line + ) + self.assertLess(directory, report) diff --git a/scripts/readiness/tests.py b/scripts/readiness/tests.py new file mode 100644 index 00000000..01e70728 --- /dev/null +++ b/scripts/readiness/tests.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Run real workspace tests, retain timings, and fail on any failed repetition.""" + +import argparse +from collections import defaultdict +import json +from pathlib import Path +import shutil +import subprocess +import time +import xml.etree.ElementTree as ET + +ROOT = Path(__file__).resolve().parents[2] + +def parse_junit(path): + tests = [] + for case in ET.parse(path).iter("testcase"): + status = "pass" + if case.find("skipped") is not None: + status = "skip" + if case.find("failure") is not None or case.find("error") is not None: + status = "fail" + tests.append({ + "name": f"{case.get('classname', '')}::{case.get('name', '')}", + "seconds": float(case.get("time", "0")), + "status": status, + }) + if not tests or not any(t["status"] != "skip" for t in tests): + raise ValueError("Test runner produced no executed tests") + return tests + +def summarize(runs): + observations = defaultdict(set) + for run in runs: + for test in run["tests"]: + observations[test["name"]].add(test["status"]) + flaky = sorted(name for name, states in observations.items() if {"pass", "fail"} <= states) + failed = any(run["exit_code"] != 0 or any(t["status"] == "fail" for t in run["tests"]) for run in runs) + return {"runs": runs, "flaky_tests": flaky, "failed": failed} + +def run(repeat, packages): + output = ROOT / "target/readiness/tests" + output.mkdir(parents=True, exist_ok=True) + runs = [] + cargo_args = ["--workspace"] if not packages else [part for p in packages for part in ("-p", p)] + for index in range(1, repeat + 1): + report = ROOT / "target/nextest/ci/junit.xml" + # Remove only the generated report, never consume a stale successful run. + report.unlink(missing_ok=True) + start = time.monotonic() + completed = subprocess.run( + ["cargo", "nextest", "run", "--locked", "--profile", "ci", *cargo_args], + cwd=ROOT, check=False, + ) + tests = [] + error = None + if report.exists(): + shutil.copyfile(report, output / f"junit-{index}.xml") + try: + tests = parse_junit(report) + except (ET.ParseError, ValueError) as exc: + error = str(exc) + else: + error = "Missing JUnit report (build or runner failure)" + runs.append({ + "iteration": index, "exit_code": completed.returncode or int(error is not None), + "wall_seconds": time.monotonic() - start, "tests": tests, "report_error": error, + }) + result = summarize(runs) + (output / "summary.json").write_text(json.dumps(result, indent=2) + "\n") + slow = sorted((t for r in runs for t in r["tests"]), key=lambda t: t["seconds"], reverse=True)[:20] + markdown = ["# Test performance", "", f"Repetitions: {repeat}", f"Flaky tests: {len(result['flaky_tests'])}", "", "| Test | Seconds | Result |", "| --- | ---: | --- |"] + markdown.extend(f"| {t['name'].replace('|', '/')} | {t['seconds']:.3f} | {t['status']} |" for t in slow) + (output / "summary.md").write_text("\n".join(markdown) + "\n") + return int(result["failed"]) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--repeat", type=int, choices=range(1, 11), default=1) + parser.add_argument("-p", "--package", action="append", default=[]) + args = parser.parse_args() + raise SystemExit(run(args.repeat, args.package)) diff --git a/src/cortex-agents/Cargo.toml b/src/cortex-agents/Cargo.toml index 5866a40f..e1251492 100644 --- a/src/cortex-agents/Cargo.toml +++ b/src/cortex-agents/Cargo.toml @@ -9,21 +9,21 @@ description = "Multi-agent system for Cortex CLI" workspace = true [dependencies] -cortex-common = { path = "../cortex-common" } +cortex-common = { workspace = true } tokio = { workspace = true } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -serde_yaml = "0.9" +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +serde_yaml = { workspace = true } toml = { workspace = true } -async-trait = "0.1" +async-trait = { workspace = true } async-recursion = "1" -tracing = "0.1" +tracing = { workspace = true } thiserror = "1" -uuid = { version = "1", features = ["v4"] } -regex = "1" -dirs = "6" -futures = "0.3" -chrono = { version = "0.4", features = ["serde"] } +uuid = { workspace = true, features = ["v4"] } +regex = { workspace = true } +dirs = { workspace = true } +futures = { workspace = true } +chrono = { workspace = true, features = ["serde"] } [dev-dependencies] -tempfile = "3" +tempfile = { workspace = true } diff --git a/src/cortex-app-server/Cargo.toml b/src/cortex-app-server/Cargo.toml index 489a5411..34936b86 100644 --- a/src/cortex-app-server/Cargo.toml +++ b/src/cortex-app-server/Cargo.toml @@ -19,13 +19,13 @@ workspace = true [dependencies] # Internal -cortex-engine = { path = "../cortex-engine" } -cortex-protocol = { path = "../cortex-protocol" } -cortex-common = { path = "../cortex-common" } +cortex-engine = { workspace = true } +cortex-protocol = { workspace = true } +cortex-common = { workspace = true } # Web framework axum = { workspace = true } -tower-http = { workspace = true } +tower-http = { workspace = true, features = ["limit"] } # Async tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "sync", "fs", "net", "process", "time"] } @@ -37,7 +37,8 @@ async-channel = { workspace = true } # Serialization serde = { workspace = true } serde_json = { workspace = true } -serde_yaml = "0.9" +serde_yaml = { workspace = true } +schemars = { workspace = true } # HTTP client reqwest = { workspace = true } @@ -48,7 +49,7 @@ thiserror = { workspace = true } # Logging tracing = { workspace = true } -tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } +tracing-subscriber = { workspace = true, features = ["env-filter", "json"] } # CLI clap = { workspace = true } @@ -61,16 +62,17 @@ dirs = "5" fs2 = "0.4" # File locking for concurrent access # Authentication -jsonwebtoken = "10" +jsonwebtoken = { version = "10", features = ["aws_lc_rs"] } # File watching -notify = "6" +notify = { workspace = true } notify-debouncer-mini = "0.4" # mDNS/Bonjour service discovery -mdns-sd = "0.11" +mdns-sd = { workspace = true } if-addrs = "0.13" gethostname = "0.5" [dev-dependencies] -tokio-test = { workspace = true } +tower = { workspace = true } +tempfile = { workspace = true } diff --git a/src/cortex-app-server/examples/export-schema.rs b/src/cortex-app-server/examples/export-schema.rs new file mode 100644 index 00000000..942a2026 --- /dev/null +++ b/src/cortex-app-server/examples/export-schema.rs @@ -0,0 +1,6 @@ +fn main() { + println!( + "{}", + serde_json::to_string_pretty(&cortex_app_server::api::schema::document()).unwrap() + ); +} diff --git a/src/cortex-app-server/src/api/files.rs b/src/cortex-app-server/src/api/files.rs index 99c23852..263ff649 100644 --- a/src/cortex-app-server/src/api/files.rs +++ b/src/cortex-app-server/src/api/files.rs @@ -10,7 +10,7 @@ use axum::{ use crate::error::{AppError, AppResult}; use crate::state::AppState; -use super::path_security::{validate_path_for_delete, validate_path_for_write}; +use super::path_security::{validate_path_for_delete, validate_path_for_write, validate_path_safe}; use super::types::{ CreateDirRequest, DeleteFileRequest, DeleteFileResponse, FileEntry, FileTreeNode, FileTreeQuery, ListFilesRequest, ListFilesResponse, ReadFileRequest, ReadFileResponse, @@ -27,7 +27,9 @@ const MAX_ENTRIES_PER_DIR: usize = 1000; pub async fn list_files(Json(req): Json) -> AppResult> { use std::fs; - let path = std::path::Path::new(&req.path); + let validated = + validate_path_safe(std::path::Path::new(&req.path)).map_err(AppError::Authorization)?; + let path = validated.as_path(); if !path.exists() { return Err(AppError::NotFound(format!("Path not found: {}", req.path))); @@ -116,7 +118,8 @@ pub async fn get_file_tree(Query(query): Query) -> AppResult