diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 437ed94835..26222462c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -279,6 +279,47 @@ npm run lint - **Imports:** Pay special attention to import paths. The project uses ESLint to enforce restrictions on relative imports between packages. +### Working with Pre-Release Versions (Development) + +This project uses **GitHub Packages** to host development and pre-release +versions of the CLI. To install these versions, you must configure your +user-level `~/.npmrc` file to authenticate with the GitHub registry. + +We have created a script to automate this process for you. + +#### Setting Up Your Environment for Development (Recommended) + +To automatically configure your environment to use the GitHub Packages registry, +run the following command: + +```bash +npm run use-dev +``` + +This command will: + +1. Check that you have the [GitHub CLI (`gh`)](https://cli.github.com/) + installed and that you are logged in. +2. **Back up** your existing `~/.npmrc` to `~/.npmrc.bak` if a backup doesn't + already exist. +3. **Automatically add** the necessary configuration to your `~/.npmrc` file. + +This setup is required for installing pre-release packages with the +`@google-gemini` scope, both locally within a project and globally (e.g., +`npm install -g @google-gemini/gemini-cli@`). + +#### Switching Back to the Production Registry + +To automatically remove the development configuration and revert to the public +npm registry (`npmjs.org`), you can run: + +```bash +npm run use-prod +``` + +This command will safely remove the lines added by the `use-dev` script from +your `~/.npmrc` file. + ### Project Structure - `packages/`: Contains the individual sub-packages of the project. diff --git a/package-lock.json b/package-lock.json index a85917a811..4460e187d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "packages/*" ], "dependencies": { + "@google-gemini/gemini-cli": "^0.11.0-nightly.20251021.0e7b3951", "@testing-library/dom": "^10.4.1", "simple-git": "^3.28.0" }, @@ -1457,6 +1458,159 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@google-gemini/gemini-cli": { + "version": "0.11.0-nightly.20251021.0e7b3951", + "resolved": "https://npm.pkg.github.com/download/@google-gemini/gemini-cli/0.11.0-nightly.20251021.0e7b3951/4dd4a330cc3538ee3a6333f57c55a795634229fe", + "integrity": "sha512-qkQ7xfH7ih9kq17IV9lQcuvp6eJxiiBZUgpEm1I2fvRHjFRzzaszcEh6D8lVqoS10sGnuj2OnI4pBZW5a8UzEQ==", + "dependencies": { + "@google-gemini/gemini-cli-core": "0.11.0-nightly.20251021.0e7b3951" + }, + "bin": { + "gemini": "bundle/gemini.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@google-gemini/gemini-cli-core": { + "version": "0.11.0-nightly.20251021.0e7b3951", + "resolved": "https://npm.pkg.github.com/download/@google-gemini/gemini-cli-core/0.11.0-nightly.20251021.0e7b3951/612a101caade1e1df620ce84e3cf83fb66f46a12", + "integrity": "sha512-pu4KiOrjM5FVCH2LnqhirOWIHodg+giOl0nASROenzFaUD1G7/FYFT9T+OY8pmGtnrfI+dNrVolp3G5mI2Ij/w==", + "dependencies": { + "@google-cloud/logging": "^11.2.1", + "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.21.0", + "@google-cloud/opentelemetry-cloud-trace-exporter": "^3.0.0", + "@google/genai": "1.16.0", + "@joshua.litt/get-ripgrep": "^0.0.2", + "@modelcontextprotocol/sdk": "^1.11.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-logs-otlp-grpc": "^0.203.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.203.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.203.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.203.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.203.0", + "@opentelemetry/instrumentation-http": "^0.203.0", + "@opentelemetry/resource-detector-gcp": "^0.40.0", + "@opentelemetry/sdk-node": "^0.203.0", + "@types/glob": "^8.1.0", + "@types/html-to-text": "^9.0.4", + "@xterm/headless": "5.5.0", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.0", + "chardet": "^2.1.0", + "diff": "^7.0.0", + "dotenv": "^17.1.0", + "fast-levenshtein": "^2.0.6", + "fast-uri": "^3.0.6", + "fdir": "^6.4.6", + "fzf": "^0.5.2", + "glob": "^10.4.5", + "google-auth-library": "^9.11.0", + "html-to-text": "^9.0.5", + "https-proxy-agent": "^7.0.6", + "ignore": "^7.0.0", + "marked": "^15.0.12", + "mime": "4.0.7", + "mnemonist": "^0.40.3", + "open": "^10.1.2", + "picomatch": "^4.0.1", + "shell-quote": "^1.8.3", + "simple-git": "^3.28.0", + "strip-ansi": "^7.1.0", + "tree-sitter-bash": "^0.25.0", + "undici": "^7.10.0", + "web-tree-sitter": "^0.25.10", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "@lydell/node-pty": "1.1.0", + "@lydell/node-pty-darwin-arm64": "1.1.0", + "@lydell/node-pty-darwin-x64": "1.1.0", + "@lydell/node-pty-linux-x64": "1.1.0", + "@lydell/node-pty-win32-arm64": "1.1.0", + "@lydell/node-pty-win32-x64": "1.1.0", + "node-pty": "^1.0.0" + } + }, + "node_modules/@google-gemini/gemini-cli-core/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@google-gemini/gemini-cli-core/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/@google-gemini/gemini-cli-core/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@google-gemini/gemini-cli-core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/@google-gemini/gemini-cli-core/node_modules/mime": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz", + "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@google-gemini/gemini-cli-core/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@google/gemini-cli": { "resolved": "packages/cli", "link": true @@ -5352,6 +5506,15 @@ "node": ">= 0.6" } }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/accepts/node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", @@ -8694,6 +8857,13 @@ "ms": "2.0.0" } }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, "node_modules/express/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -16323,6 +16493,15 @@ "node": ">= 0.6" } }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/type-is/node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", diff --git a/package.json b/package.json index 47657d3f14..d121587ed3 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,9 @@ "telemetry": "node scripts/telemetry.js", "check:lockfile": "node scripts/check-lockfile.js", "clean": "node scripts/clean.js", - "pre-commit": "node scripts/pre-commit.js" + "pre-commit": "node scripts/pre-commit.js", + "use-dev": "node scripts/configure-registry.js dev", + "use-prod": "node scripts/configure-registry.js prod" }, "overrides": { "wrap-ansi": "9.0.2", @@ -111,6 +113,7 @@ "yargs": "^17.7.2" }, "dependencies": { + "@google-gemini/gemini-cli": "^0.11.0-nightly.20251021.0e7b3951", "@testing-library/dom": "^10.4.1", "simple-git": "^3.28.0" }, diff --git a/scripts/configure-registry.js b/scripts/configure-registry.js new file mode 100644 index 0000000000..45a7a55985 --- /dev/null +++ b/scripts/configure-registry.js @@ -0,0 +1,147 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import os from 'node:os'; +import { execSync } from 'node:child_process'; + +const arg = process.argv[2]; +const homedir = os.homedir(); +const npmrcPath = path.join(homedir, '.npmrc'); +const backupPath = path.join(homedir, '.npmrc.bak'); + +const GITHUB_REGISTRY_URL = 'https://npm.pkg.github.com/'; +const GITHUB_SCOPE = '@google-gemini'; +const REGISTRY_LINE = `${GITHUB_SCOPE}:registry=${GITHUB_REGISTRY_URL}`; + +function checkGhCli() { + try { + execSync('gh --version', { stdio: 'ignore' }); + return true; + } catch (_e) { + console.error( + 'Error: The GitHub CLI (`gh`) is not installed or not in your PATH.', + ); + console.error('Please install it to continue: https://cli.github.com/'); + return false; + } +} + +function getGhAuthStatus() { + try { + execSync('gh auth status', { stdio: 'ignore' }); + return true; + } catch (_e) { + console.error('Error: You are not logged in to the GitHub CLI (`gh`).'); + console.error( + "Please run `gh auth login` and ensure you grant the 'read:packages' and 'write:packages' scopes.", + ); + return false; + } +} + +function getGhAuthToken() { + try { + const token = execSync('gh auth token', { + encoding: 'utf8', + }).trim(); + if (!token) { + throw new Error('No token returned from `gh auth token`'); + } + return token; + } catch (_e) { + console.error('Error: Failed to retrieve GitHub auth token.'); + console.error( + 'Please ensure you are logged in (`gh auth status`) and have the correct permissions.', + ); + return null; + } +} + +function setupDev() { + console.log( + 'Configuring your global ~/.npmrc for `dev` (GitHub Packages)...', + ); + + if (!checkGhCli() || !getGhAuthStatus()) { + process.exit(1); + } + + const token = getGhAuthToken(); + if (!token) { + process.exit(1); + } + + const AUTH_LINE = `//npm.pkg.github.com/:_authToken=${token}`; + let npmrcContent = ''; + + if (fs.existsSync(npmrcPath)) { + npmrcContent = fs.readFileSync(npmrcPath, 'utf-8'); + if (npmrcContent.includes(REGISTRY_LINE)) { + console.log('✅ Your ~/.npmrc file is already configured for dev.'); + return; + } + // Create a backup if one doesn't already exist + if (!fs.existsSync(backupPath)) { + fs.copyFileSync(npmrcPath, backupPath); + console.log(`Backed up your existing configuration to ${backupPath}`); + } + } + + const newContent = [ + npmrcContent, + `\n# Added by gemini-cli for dev environment`, + REGISTRY_LINE, + AUTH_LINE, + ] + .join('\n') + .trim(); + + fs.writeFileSync(npmrcPath, newContent + '\n'); + console.log(`✅ Successfully updated your ~/.npmrc file.`); + console.log( + 'You can now install pre-release packages, e.g., `npm install -g @google-gemini/gemini-cli@`', + ); +} + +function setupProd() { + console.log('Configuring your global ~/.npmrc for `prod` (npmjs.org)...'); + if (!fs.existsSync(npmrcPath)) { + console.log('No ~/.npmrc file found. Already configured for prod.'); + return; + } + + let npmrcContent = fs.readFileSync(npmrcPath, 'utf-8'); + const lines = npmrcContent.split('\n'); + const filteredLines = lines.filter( + (line) => + !line.includes('npm.pkg.github.com') && + !line.includes('@google-gemini:registry') && + !line.includes('# Added by gemini-cli'), + ); + + if (lines.length === filteredLines.length) { + console.log('✅ Your ~/.npmrc file is already configured for prod.'); + return; + } + + fs.writeFileSync(npmrcPath, filteredLines.join('\n').trim() + '\n'); + console.log(`✅ Successfully cleaned dev configuration from ~/.npmrc.`); + if (fs.existsSync(backupPath)) { + console.log(`Your original configuration was saved to ${backupPath}`); + } +} + +if (arg === 'dev') { + setupDev(); +} else if (arg === 'prod') { + setupProd(); +} else { + console.error('Invalid argument. Please use `dev` or `prod`.'); + console.error('Usage: node scripts/configure-registry.js '); + process.exit(1); +}