Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3496ea409 | |||
| efb6c3aa89 | |||
| ac454bcfbc | |||
| c7a70e6421 | |||
| 611d934829 | |||
| 9c2fd5a7c6 | |||
| 3db35812b7 | |||
| 8380f0a3b1 | |||
| ef247e220d | |||
| 39938000a9 | |||
| d128fb0f7b | |||
| 37903d5dbb | |||
| a3821a9440 | |||
| 0bf3a015e8 | |||
| 28c9a907de | |||
| f9f916e1dc | |||
| 78dfe9dea8 | |||
| c9d07b62f2 | |||
| 189ab6c637 | |||
| 4aec3cdb24 | |||
| b765fb8af4 | |||
| fb0c7d268f | |||
| be12371380 | |||
| a037b961b1 | |||
| 6c739955c0 |
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"experimental": {
|
||||
"plan": true,
|
||||
"extensionReloading": true
|
||||
"extensionReloading": true,
|
||||
"modelSteering": true
|
||||
},
|
||||
"general": {
|
||||
"devtools": true
|
||||
|
||||
@@ -59,6 +59,10 @@ To standardize the process of updating changelog files (`latest.md`,
|
||||
|
||||
*Use this path if the version number ends in `.0`.*
|
||||
|
||||
**Important:** Based on the version, you must choose to follow either section
|
||||
A.1 for stable releases or A.2 for preview releases. Do not follow the
|
||||
instructions for the other section.
|
||||
|
||||
### A.1: Stable Release (e.g., `v0.28.0`)
|
||||
|
||||
For a stable release, you will generate two distinct summaries from the
|
||||
@@ -73,7 +77,8 @@ detailed **highlights** section for the release-specific page.
|
||||
use the existing announcements in `docs/changelogs/index.md` and the
|
||||
example within
|
||||
`.gemini/skills/docs-changelog/references/index_template.md` as your
|
||||
guide. This format includes PR links and authors.
|
||||
guide. This format includes PR links and authors. Stick to 1 or 2 PR
|
||||
links and authors.
|
||||
- Add this new announcement to the top of `docs/changelogs/index.md`.
|
||||
|
||||
2. **Create Highlights and Update `latest.md`**:
|
||||
@@ -105,6 +110,10 @@ detailed **highlights** section for the release-specific page.
|
||||
|
||||
*Use this path if the version number does **not** end in `.0`.*
|
||||
|
||||
**Important:** Based on the version, you must choose to follow either section
|
||||
B.1 for stable patches or B.2 for preview patches. Do not follow the
|
||||
instructions for the other section.
|
||||
|
||||
### B.1: Stable Patch (e.g., `v0.28.1`)
|
||||
|
||||
- **Target File**: `docs/changelogs/latest.md`
|
||||
@@ -113,10 +122,12 @@ detailed **highlights** section for the release-specific page.
|
||||
`# Latest stable release: {{version}}`
|
||||
2. Update the rease date. The line should read,
|
||||
`Released: {{release_date_month_dd_yyyy}}`
|
||||
3. **Prepend** the processed "What's Changed" list from the temporary file
|
||||
3. Determine if a "What's Changed" section exists in the temporary file
|
||||
If so, continue to step 4. Otherwise, skip to step 5.
|
||||
4. **Prepend** the processed "What's Changed" list from the temporary file
|
||||
to the existing "What's Changed" list in `latest.md`. Do not change or
|
||||
replace the existing list, **only add** to the beginning of it.
|
||||
4. In the "Full Changelog", edit **only** the end of the URL. Identify the
|
||||
5. In the "Full Changelog", edit **only** the end of the URL. Identify the
|
||||
last part of the URL that looks like `...{previous_version}` and update
|
||||
it to be `...{version}`.
|
||||
|
||||
@@ -133,10 +144,12 @@ detailed **highlights** section for the release-specific page.
|
||||
`# Preview release: {{version}}`
|
||||
2. Update the rease date. The line should read,
|
||||
`Released: {{release_date_month_dd_yyyy}}`
|
||||
3. **Prepend** the processed "What's Changed" list from the temporary file
|
||||
3. Determine if a "What's Changed" section exists in the temporary file
|
||||
If so, continue to step 4. Otherwise, skip to step 5.
|
||||
4. **Prepend** the processed "What's Changed" list from the temporary file
|
||||
to the existing "What's Changed" list in `preview.md`. Do not change or
|
||||
replace the existing list, **only add** to the beginning of it.
|
||||
4. In the "Full Changelog", edit **only** the end of the URL. Identify the
|
||||
5. In the "Full Changelog", edit **only** the end of the URL. Identify the
|
||||
last part of the URL that looks like `...{previous_version}` and update
|
||||
it to be `...{version}`.
|
||||
|
||||
@@ -149,5 +162,5 @@ detailed **highlights** section for the release-specific page.
|
||||
|
||||
## Finalize
|
||||
|
||||
- After making changes, run `npm run format` to ensure consistency.
|
||||
- After making changes, run `npm run format` ONLY to ensure consistency.
|
||||
- Delete any temporary files created during the process.
|
||||
|
||||
@@ -39,18 +39,22 @@ runs:
|
||||
if: "inputs.dry-run != 'true'"
|
||||
env:
|
||||
GH_TOKEN: '${{ inputs.github-token }}'
|
||||
INPUTS_BRANCH_NAME: ${{ inputs.branch-name }}
|
||||
INPUTS_PR_TITLE: ${{ inputs.pr-title }}
|
||||
INPUTS_PR_BODY: ${{ inputs.pr-body }}
|
||||
INPUTS_BASE_BRANCH: ${{ inputs.base-branch }}
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
set -e
|
||||
if ! git ls-remote --exit-code --heads origin "${{ inputs.branch-name }}"; then
|
||||
echo "::error::Branch '${{ inputs.branch-name }}' does not exist on the remote repository."
|
||||
if ! git ls-remote --exit-code --heads origin "${INPUTS_BRANCH_NAME}"; then
|
||||
echo "::error::Branch '${INPUTS_BRANCH_NAME}' does not exist on the remote repository."
|
||||
exit 1
|
||||
fi
|
||||
PR_URL=$(gh pr create \
|
||||
--title "${{ inputs.pr-title }}" \
|
||||
--body "${{ inputs.pr-body }}" \
|
||||
--base "${{ inputs.base-branch }}" \
|
||||
--head "${{ inputs.branch-name }}" \
|
||||
--title "${INPUTS_PR_TITLE}" \
|
||||
--body "${INPUTS_PR_BODY}" \
|
||||
--base "${INPUTS_BASE_BRANCH}" \
|
||||
--head "${INPUTS_BRANCH_NAME}" \
|
||||
--fill)
|
||||
gh pr merge "$PR_URL" --auto
|
||||
|
||||
@@ -30,16 +30,22 @@ runs:
|
||||
id: 'npm_auth_token'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
AUTH_TOKEN="${{ inputs.github-token }}"
|
||||
PACKAGE_NAME="${{ inputs.package-name }}"
|
||||
AUTH_TOKEN="${INPUTS_GITHUB_TOKEN}"
|
||||
PACKAGE_NAME="${INPUTS_PACKAGE_NAME}"
|
||||
PRIVATE_REPO="@google-gemini/"
|
||||
if [[ "$PACKAGE_NAME" == "$PRIVATE_REPO"* ]]; then
|
||||
AUTH_TOKEN="${{ inputs.github-token }}"
|
||||
AUTH_TOKEN="${INPUTS_GITHUB_TOKEN}"
|
||||
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli" ]]; then
|
||||
AUTH_TOKEN="${{ inputs.wombat-token-cli }}"
|
||||
AUTH_TOKEN="${INPUTS_WOMBAT_TOKEN_CLI}"
|
||||
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli-core" ]]; then
|
||||
AUTH_TOKEN="${{ inputs.wombat-token-core }}"
|
||||
AUTH_TOKEN="${INPUTS_WOMBAT_TOKEN_CORE}"
|
||||
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli-a2a-server" ]]; then
|
||||
AUTH_TOKEN="${{ inputs.wombat-token-a2a-server }}"
|
||||
AUTH_TOKEN="${INPUTS_WOMBAT_TOKEN_A2A_SERVER}"
|
||||
fi
|
||||
echo "auth-token=$AUTH_TOKEN" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
INPUTS_GITHUB_TOKEN: ${{ inputs.github-token }}
|
||||
INPUTS_PACKAGE_NAME: ${{ inputs.package-name }}
|
||||
INPUTS_WOMBAT_TOKEN_CLI: ${{ inputs.wombat-token-cli }}
|
||||
INPUTS_WOMBAT_TOKEN_CORE: ${{ inputs.wombat-token-core }}
|
||||
INPUTS_WOMBAT_TOKEN_A2A_SERVER: ${{ inputs.wombat-token-a2a-server }}
|
||||
|
||||
@@ -93,15 +93,19 @@ runs:
|
||||
id: 'release_branch'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
BRANCH_NAME="release/${{ inputs.release-tag }}"
|
||||
BRANCH_NAME="release/${INPUTS_RELEASE_TAG}"
|
||||
git switch -c "${BRANCH_NAME}"
|
||||
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
|
||||
env:
|
||||
INPUTS_RELEASE_TAG: ${{ inputs.release-tag }}
|
||||
|
||||
- name: '⬆️ Update package versions'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm run release:version "${{ inputs.release-version }}"
|
||||
npm run release:version "${INPUTS_RELEASE_VERSION}"
|
||||
env:
|
||||
INPUTS_RELEASE_VERSION: ${{ inputs.release-version }}
|
||||
|
||||
- name: '💾 Commit and Conditionally Push package versions'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
@@ -163,23 +167,30 @@ runs:
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}'
|
||||
INPUTS_DRY_RUN: ${{ inputs.dry-run }}
|
||||
INPUTS_CORE_PACKAGE_NAME: ${{ inputs.core-package-name }}
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm publish \
|
||||
--dry-run="${{ inputs.dry-run }}" \
|
||||
--workspace="${{ inputs.core-package-name }}" \
|
||||
--dry-run="${INPUTS_DRY_RUN}" \
|
||||
--workspace="${INPUTS_CORE_PACKAGE_NAME}" \
|
||||
--no-tag
|
||||
npm dist-tag rm ${{ inputs.core-package-name }} false --silent
|
||||
npm dist-tag rm ${INPUTS_CORE_PACKAGE_NAME} false --silent
|
||||
|
||||
- name: '🔗 Install latest core package'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
if: "${{ inputs.dry-run != 'true' }}"
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm install "${{ inputs.core-package-name }}@${{ inputs.release-version }}" \
|
||||
--workspace="${{ inputs.cli-package-name }}" \
|
||||
--workspace="${{ inputs.a2a-package-name }}" \
|
||||
npm install "${INPUTS_CORE_PACKAGE_NAME}@${INPUTS_RELEASE_VERSION}" \
|
||||
--workspace="${INPUTS_CLI_PACKAGE_NAME}" \
|
||||
--workspace="${INPUTS_A2A_PACKAGE_NAME}" \
|
||||
--save-exact
|
||||
env:
|
||||
INPUTS_CORE_PACKAGE_NAME: ${{ inputs.core-package-name }}
|
||||
INPUTS_RELEASE_VERSION: ${{ inputs.release-version }}
|
||||
INPUTS_CLI_PACKAGE_NAME: ${{ inputs.cli-package-name }}
|
||||
INPUTS_A2A_PACKAGE_NAME: ${{ inputs.a2a-package-name }}
|
||||
|
||||
- name: 'Get CLI Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
@@ -195,13 +206,15 @@ runs:
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}'
|
||||
INPUTS_DRY_RUN: ${{ inputs.dry-run }}
|
||||
INPUTS_CLI_PACKAGE_NAME: ${{ inputs.cli-package-name }}
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm publish \
|
||||
--dry-run="${{ inputs.dry-run }}" \
|
||||
--workspace="${{ inputs.cli-package-name }}" \
|
||||
--dry-run="${INPUTS_DRY_RUN}" \
|
||||
--workspace="${INPUTS_CLI_PACKAGE_NAME}" \
|
||||
--no-tag
|
||||
npm dist-tag rm ${{ inputs.cli-package-name }} false --silent
|
||||
npm dist-tag rm ${INPUTS_CLI_PACKAGE_NAME} false --silent
|
||||
|
||||
- name: 'Get a2a-server Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
@@ -217,14 +230,16 @@ runs:
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}'
|
||||
INPUTS_DRY_RUN: ${{ inputs.dry-run }}
|
||||
INPUTS_A2A_PACKAGE_NAME: ${{ inputs.a2a-package-name }}
|
||||
shell: 'bash'
|
||||
# Tag staging for initial release
|
||||
run: |
|
||||
npm publish \
|
||||
--dry-run="${{ inputs.dry-run }}" \
|
||||
--workspace="${{ inputs.a2a-package-name }}" \
|
||||
--dry-run="${INPUTS_DRY_RUN}" \
|
||||
--workspace="${INPUTS_A2A_PACKAGE_NAME}" \
|
||||
--no-tag
|
||||
npm dist-tag rm ${{ inputs.a2a-package-name }} false --silent
|
||||
npm dist-tag rm ${INPUTS_A2A_PACKAGE_NAME} false --silent
|
||||
|
||||
- name: '🔬 Verify NPM release by version'
|
||||
uses: './.github/actions/verify-release'
|
||||
@@ -258,13 +273,16 @@ runs:
|
||||
if: "${{ inputs.dry-run != 'true' && inputs.skip-github-release != 'true' && inputs.npm-tag != 'dev' && inputs.npm-registry-url != 'https://npm.pkg.github.com/' }}"
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ inputs.github-release-token || inputs.github-token }}'
|
||||
INPUTS_RELEASE_TAG: ${{ inputs.release-tag }}
|
||||
STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME: ${{ steps.release_branch.outputs.BRANCH_NAME }}
|
||||
INPUTS_PREVIOUS_TAG: ${{ inputs.previous-tag }}
|
||||
shell: 'bash'
|
||||
run: |
|
||||
gh release create "${{ inputs.release-tag }}" \
|
||||
gh release create "${INPUTS_RELEASE_TAG}" \
|
||||
bundle/gemini.js \
|
||||
--target "${{ steps.release_branch.outputs.BRANCH_NAME }}" \
|
||||
--title "Release ${{ inputs.release-tag }}" \
|
||||
--notes-start-tag "${{ inputs.previous-tag }}" \
|
||||
--target "${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}" \
|
||||
--title "Release ${INPUTS_RELEASE_TAG}" \
|
||||
--notes-start-tag "${INPUTS_PREVIOUS_TAG}" \
|
||||
--generate-notes \
|
||||
${{ inputs.npm-tag != 'latest' && '--prerelease' || '' }}
|
||||
|
||||
@@ -274,5 +292,8 @@ runs:
|
||||
continue-on-error: true
|
||||
shell: 'bash'
|
||||
run: |
|
||||
echo "Cleaning up release branch ${{ steps.release_branch.outputs.BRANCH_NAME }}..."
|
||||
git push origin --delete "${{ steps.release_branch.outputs.BRANCH_NAME }}"
|
||||
echo "Cleaning up release branch ${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}..."
|
||||
git push origin --delete "${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}"
|
||||
|
||||
env:
|
||||
STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME: ${{ steps.release_branch.outputs.BRANCH_NAME }}
|
||||
|
||||
@@ -52,8 +52,10 @@ runs:
|
||||
id: 'branch_name'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
REF_NAME="${{ inputs.ref-name }}"
|
||||
REF_NAME="${INPUTS_REF_NAME}"
|
||||
echo "name=${REF_NAME%/merge}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
INPUTS_REF_NAME: ${{ inputs.ref-name }}
|
||||
- name: 'Build and Push the Docker Image'
|
||||
uses: 'docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83' # ratchet:docker/build-push-action@v6
|
||||
with:
|
||||
|
||||
@@ -56,8 +56,8 @@ runs:
|
||||
id: 'image_tag'
|
||||
shell: 'bash'
|
||||
run: |-
|
||||
SHELL_TAG_NAME="${{ inputs.github-ref-name }}"
|
||||
FINAL_TAG="${{ inputs.github-sha }}"
|
||||
SHELL_TAG_NAME="${INPUTS_GITHUB_REF_NAME}"
|
||||
FINAL_TAG="${INPUTS_GITHUB_SHA}"
|
||||
if [[ "$SHELL_TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "Release detected."
|
||||
FINAL_TAG="${SHELL_TAG_NAME#v}"
|
||||
@@ -66,15 +66,19 @@ runs:
|
||||
fi
|
||||
echo "Determined image tag: $FINAL_TAG"
|
||||
echo "FINAL_TAG=$FINAL_TAG" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
INPUTS_GITHUB_REF_NAME: ${{ inputs.github-ref-name }}
|
||||
INPUTS_GITHUB_SHA: ${{ inputs.github-sha }}
|
||||
- name: 'build'
|
||||
id: 'docker_build'
|
||||
shell: 'bash'
|
||||
env:
|
||||
GEMINI_SANDBOX_IMAGE_TAG: '${{ steps.image_tag.outputs.FINAL_TAG }}'
|
||||
GEMINI_SANDBOX: 'docker'
|
||||
STEPS_IMAGE_TAG_OUTPUTS_FINAL_TAG: ${{ steps.image_tag.outputs.FINAL_TAG }}
|
||||
run: |-
|
||||
npm run build:sandbox -- \
|
||||
--image google/gemini-cli-sandbox:${{ steps.image_tag.outputs.FINAL_TAG }} \
|
||||
--image google/gemini-cli-sandbox:${STEPS_IMAGE_TAG_OUTPUTS_FINAL_TAG} \
|
||||
--output-file final_image_uri.txt
|
||||
echo "uri=$(cat final_image_uri.txt)" >> $GITHUB_OUTPUT
|
||||
- name: 'verify'
|
||||
@@ -89,7 +93,9 @@ runs:
|
||||
shell: 'bash'
|
||||
if: "${{ inputs.dry-run != 'true' }}"
|
||||
run: |-
|
||||
docker push "${{ steps.docker_build.outputs.uri }}"
|
||||
docker push "${STEPS_DOCKER_BUILD_OUTPUTS_URI}"
|
||||
env:
|
||||
STEPS_DOCKER_BUILD_OUTPUTS_URI: ${{ steps.docker_build.outputs.uri }}
|
||||
- name: 'Create issue on failure'
|
||||
if: |-
|
||||
${{ failure() }}
|
||||
|
||||
@@ -18,5 +18,7 @@ runs:
|
||||
shell: 'bash'
|
||||
run: |-
|
||||
echo ""@google-gemini:registry=https://npm.pkg.github.com"" > ~/.npmrc
|
||||
echo ""//npm.pkg.github.com/:_authToken=${{ inputs.github-token }}"" >> ~/.npmrc
|
||||
echo ""//npm.pkg.github.com/:_authToken=${INPUTS_GITHUB_TOKEN}"" >> ~/.npmrc
|
||||
echo ""@google:registry=https://wombat-dressing-room.appspot.com"" >> ~/.npmrc
|
||||
env:
|
||||
INPUTS_GITHUB_TOKEN: ${{ inputs.github-token }}
|
||||
|
||||
@@ -71,10 +71,13 @@ runs:
|
||||
${{ inputs.dry-run != 'true' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}'
|
||||
INPUTS_CORE_PACKAGE_NAME: ${{ inputs.core-package-name }}
|
||||
INPUTS_VERSION: ${{ inputs.version }}
|
||||
INPUTS_CHANNEL: ${{ inputs.channel }}
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
npm dist-tag add ${{ inputs.core-package-name }}@${{ inputs.version }} ${{ inputs.channel }}
|
||||
npm dist-tag add ${INPUTS_CORE_PACKAGE_NAME}@${INPUTS_VERSION} ${INPUTS_CHANNEL}
|
||||
|
||||
- name: 'Get cli Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
@@ -91,10 +94,13 @@ runs:
|
||||
${{ inputs.dry-run != 'true' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}'
|
||||
INPUTS_CLI_PACKAGE_NAME: ${{ inputs.cli-package-name }}
|
||||
INPUTS_VERSION: ${{ inputs.version }}
|
||||
INPUTS_CHANNEL: ${{ inputs.channel }}
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
npm dist-tag add ${{ inputs.cli-package-name }}@${{ inputs.version }} ${{ inputs.channel }}
|
||||
npm dist-tag add ${INPUTS_CLI_PACKAGE_NAME}@${INPUTS_VERSION} ${INPUTS_CHANNEL}
|
||||
|
||||
- name: 'Get a2a Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
@@ -111,10 +117,13 @@ runs:
|
||||
${{ inputs.dry-run == 'false' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}'
|
||||
INPUTS_A2A_PACKAGE_NAME: ${{ inputs.a2a-package-name }}
|
||||
INPUTS_VERSION: ${{ inputs.version }}
|
||||
INPUTS_CHANNEL: ${{ inputs.channel }}
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
npm dist-tag add ${{ inputs.a2a-package-name }}@${{ inputs.version }} ${{ inputs.channel }}
|
||||
npm dist-tag add ${INPUTS_A2A_PACKAGE_NAME}@${INPUTS_VERSION} ${INPUTS_CHANNEL}
|
||||
|
||||
- name: 'Log dry run'
|
||||
if: |-
|
||||
@@ -122,4 +131,15 @@ runs:
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
echo "Dry run: Would have added tag '${{ inputs.channel }}' to version '${{ inputs.version }}' for ${{ inputs.cli-package-name }}, ${{ inputs.core-package-name }}, and ${{ inputs.a2a-package-name }}."
|
||||
echo "Dry run: Would have added tag '${INPUTS_CHANNEL}' to version '${INPUTS_VERSION}' for ${INPUTS_CLI_PACKAGE_NAME}, ${INPUTS_CORE_PACKAGE_NAME}, and ${INPUTS_A2A_PACKAGE_NAME}."
|
||||
|
||||
env:
|
||||
INPUTS_CHANNEL: ${{ inputs.channel }}
|
||||
|
||||
INPUTS_VERSION: ${{ inputs.version }}
|
||||
|
||||
INPUTS_CLI_PACKAGE_NAME: ${{ inputs.cli-package-name }}
|
||||
|
||||
INPUTS_CORE_PACKAGE_NAME: ${{ inputs.core-package-name }}
|
||||
|
||||
INPUTS_A2A_PACKAGE_NAME: ${{ inputs.a2a-package-name }}
|
||||
|
||||
@@ -64,10 +64,13 @@ runs:
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |-
|
||||
gemini_version=$(gemini --version)
|
||||
if [ "$gemini_version" != "${{ inputs.expected-version }}" ]; then
|
||||
echo "❌ NPM Version mismatch: Got $gemini_version from ${{ inputs.npm-package }}, expected ${{ inputs.expected-version }}"
|
||||
if [ "$gemini_version" != "${INPUTS_EXPECTED_VERSION}" ]; then
|
||||
echo "❌ NPM Version mismatch: Got $gemini_version from ${INPUTS_NPM_PACKAGE}, expected ${INPUTS_EXPECTED_VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
INPUTS_EXPECTED_VERSION: ${{ inputs.expected-version }}
|
||||
INPUTS_NPM_PACKAGE: ${{ inputs.npm-package }}
|
||||
|
||||
- name: 'Clear npm cache'
|
||||
shell: 'bash'
|
||||
@@ -77,11 +80,14 @@ runs:
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |-
|
||||
gemini_version=$(npx --prefer-online "${{ inputs.npm-package}}" --version)
|
||||
if [ "$gemini_version" != "${{ inputs.expected-version }}" ]; then
|
||||
echo "❌ NPX Run Version mismatch: Got $gemini_version from ${{ inputs.npm-package }}, expected ${{ inputs.expected-version }}"
|
||||
gemini_version=$(npx --prefer-online "${INPUTS_NPM_PACKAGE}" --version)
|
||||
if [ "$gemini_version" != "${INPUTS_EXPECTED_VERSION}" ]; then
|
||||
echo "❌ NPX Run Version mismatch: Got $gemini_version from ${INPUTS_NPM_PACKAGE}, expected ${INPUTS_EXPECTED_VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
INPUTS_NPM_PACKAGE: ${{ inputs.npm-package }}
|
||||
INPUTS_EXPECTED_VERSION: ${{ inputs.expected-version }}
|
||||
|
||||
- name: 'Install dependencies for integration tests'
|
||||
shell: 'bash'
|
||||
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
REPO_NAME: '${{ github.event.inputs.repo_name }}'
|
||||
run: |
|
||||
mkdir -p ./pr
|
||||
echo '${{ env.REPO_NAME }}' > ./pr/repo_name
|
||||
echo '${REPO_NAME}' > ./pr/repo_name
|
||||
- uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'repo_name'
|
||||
@@ -320,14 +320,19 @@ jobs:
|
||||
steps:
|
||||
- name: 'Check E2E test results'
|
||||
run: |
|
||||
if [[ ${{ needs.e2e_linux.result }} != 'success' || \
|
||||
${{ needs.e2e_mac.result }} != 'success' || \
|
||||
${{ needs.e2e_windows.result }} != 'success' || \
|
||||
${{ needs.evals.result }} != 'success' ]]; then
|
||||
if [[ ${NEEDS_E2E_LINUX_RESULT} != 'success' || \
|
||||
${NEEDS_E2E_MAC_RESULT} != 'success' || \
|
||||
${NEEDS_E2E_WINDOWS_RESULT} != 'success' || \
|
||||
${NEEDS_EVALS_RESULT} != 'success' ]]; then
|
||||
echo "One or more E2E jobs failed."
|
||||
exit 1
|
||||
fi
|
||||
echo "All required E2E jobs passed!"
|
||||
env:
|
||||
NEEDS_E2E_LINUX_RESULT: ${{ needs.e2e_linux.result }}
|
||||
NEEDS_E2E_MAC_RESULT: ${{ needs.e2e_mac.result }}
|
||||
NEEDS_E2E_WINDOWS_RESULT: ${{ needs.e2e_windows.result }}
|
||||
NEEDS_EVALS_RESULT: ${{ needs.evals.result }}
|
||||
|
||||
set_workflow_status:
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
|
||||
@@ -464,14 +464,22 @@ jobs:
|
||||
steps:
|
||||
- name: 'Check all job results'
|
||||
run: |
|
||||
if [[ (${{ needs.lint.result }} != 'success' && ${{ needs.lint.result }} != 'skipped') || \
|
||||
(${{ needs.link_checker.result }} != 'success' && ${{ needs.link_checker.result }} != 'skipped') || \
|
||||
(${{ needs.test_linux.result }} != 'success' && ${{ needs.test_linux.result }} != 'skipped') || \
|
||||
(${{ needs.test_mac.result }} != 'success' && ${{ needs.test_mac.result }} != 'skipped') || \
|
||||
(${{ needs.test_windows.result }} != 'success' && ${{ needs.test_windows.result }} != 'skipped') || \
|
||||
(${{ needs.codeql.result }} != 'success' && ${{ needs.codeql.result }} != 'skipped') || \
|
||||
(${{ needs.bundle_size.result }} != 'success' && ${{ needs.bundle_size.result }} != 'skipped') ]]; then
|
||||
if [[ (${NEEDS_LINT_RESULT} != 'success' && ${NEEDS_LINT_RESULT} != 'skipped') || \
|
||||
(${NEEDS_LINK_CHECKER_RESULT} != 'success' && ${NEEDS_LINK_CHECKER_RESULT} != 'skipped') || \
|
||||
(${NEEDS_TEST_LINUX_RESULT} != 'success' && ${NEEDS_TEST_LINUX_RESULT} != 'skipped') || \
|
||||
(${NEEDS_TEST_MAC_RESULT} != 'success' && ${NEEDS_TEST_MAC_RESULT} != 'skipped') || \
|
||||
(${NEEDS_TEST_WINDOWS_RESULT} != 'success' && ${NEEDS_TEST_WINDOWS_RESULT} != 'skipped') || \
|
||||
(${NEEDS_CODEQL_RESULT} != 'success' && ${NEEDS_CODEQL_RESULT} != 'skipped') || \
|
||||
(${NEEDS_BUNDLE_SIZE_RESULT} != 'success' && ${NEEDS_BUNDLE_SIZE_RESULT} != 'skipped') ]]; then
|
||||
echo "One or more CI jobs failed."
|
||||
exit 1
|
||||
fi
|
||||
echo "All CI jobs passed!"
|
||||
env:
|
||||
NEEDS_LINT_RESULT: ${{ needs.lint.result }}
|
||||
NEEDS_LINK_CHECKER_RESULT: ${{ needs.link_checker.result }}
|
||||
NEEDS_TEST_LINUX_RESULT: ${{ needs.test_linux.result }}
|
||||
NEEDS_TEST_MAC_RESULT: ${{ needs.test_mac.result }}
|
||||
NEEDS_TEST_WINDOWS_RESULT: ${{ needs.test_windows.result }}
|
||||
NEEDS_CODEQL_RESULT: ${{ needs.codeql.result }}
|
||||
NEEDS_BUNDLE_SIZE_RESULT: ${{ needs.bundle_size.result }}
|
||||
|
||||
@@ -68,10 +68,10 @@ jobs:
|
||||
VERBOSE: 'true'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
if [[ "${{ env.IS_DOCKER }}" == "true" ]]; then
|
||||
npm run deflake:test:integration:sandbox:docker -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
|
||||
if [[ "${IS_DOCKER}" == "true" ]]; then
|
||||
npm run deflake:test:integration:sandbox:docker -- --runs="${RUNS}" -- --testNamePattern "'${TEST_NAME_PATTERN}'"
|
||||
else
|
||||
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
|
||||
npm run deflake:test:integration:sandbox:none -- --runs="${RUNS}" -- --testNamePattern "'${TEST_NAME_PATTERN}'"
|
||||
fi
|
||||
|
||||
deflake_e2e_mac:
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
|
||||
VERBOSE: 'true'
|
||||
run: |
|
||||
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
|
||||
npm run deflake:test:integration:sandbox:none -- --runs="${RUNS}" -- --testNamePattern "'${TEST_NAME_PATTERN}'"
|
||||
|
||||
deflake_e2e_windows:
|
||||
name: 'Slow E2E - Win'
|
||||
@@ -167,4 +167,4 @@ jobs:
|
||||
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
|
||||
shell: 'pwsh'
|
||||
run: |
|
||||
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
|
||||
npm run deflake:test:integration:sandbox:none -- --runs="$env:RUNS" -- --testNamePattern "'$env:TEST_NAME_PATTERN'"
|
||||
|
||||
@@ -44,5 +44,5 @@ jobs:
|
||||
- name: 'Run evaluation'
|
||||
working-directory: '/app'
|
||||
run: |
|
||||
poetry run exp_run --experiment-mode=on-demand --branch-or-commit=${{ github.ref_name }} --model-name=gemini-2.5-pro --dataset=swebench_verified --concurrency=15
|
||||
poetry run exp_run --experiment-mode=on-demand --branch-or-commit=${GITHUB_REF_NAME} --model-name=gemini-2.5-pro --dataset=swebench_verified --concurrency=15
|
||||
poetry run python agent_prototypes/scripts/parse_gcli_logs_experiment.py --experiment_dir=experiments/adhoc/gcli_temp_exp --gcs-bucket="${EVAL_GCS_BUCKET}" --gcs-path=gh_action_artifacts
|
||||
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
|
||||
run: |
|
||||
CMD="npm run test:all_evals"
|
||||
PATTERN="${{ env.TEST_NAME_PATTERN }}"
|
||||
PATTERN="${TEST_NAME_PATTERN}"
|
||||
|
||||
if [[ -n "$PATTERN" ]]; then
|
||||
if [[ "$PATTERN" == *.ts || "$PATTERN" == *.js || "$PATTERN" == */* ]]; then
|
||||
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
},
|
||||
"coreTools": [
|
||||
"run_shell_command(echo)"
|
||||
],
|
||||
]
|
||||
}
|
||||
prompt: |-
|
||||
## Role
|
||||
|
||||
@@ -56,7 +56,18 @@ jobs:
|
||||
GH_TOKEN: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
BODY: '${{ github.event.inputs.body || github.event.release.body }}'
|
||||
|
||||
- name: 'Validate version'
|
||||
id: 'validate_version'
|
||||
run: |
|
||||
if echo "${{ steps.release_info.outputs.VERSION }}" | grep -q "nightly"; then
|
||||
echo "Nightly release detected. Stopping workflow."
|
||||
echo "CONTINUE=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "CONTINUE=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: 'Generate Changelog with Gemini'
|
||||
if: "steps.validate_version.outputs.CONTINUE == 'true'"
|
||||
uses: 'google-github-actions/run-gemini-cli@a3bf79042542528e91937b3a3a6fbc4967ee3c31' # ratchet:google-github-actions/run-gemini-cli@v0
|
||||
with:
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
@@ -70,7 +81,10 @@ jobs:
|
||||
|
||||
Execute the release notes generation process using the information provided.
|
||||
|
||||
When you are done, please output your thought process and the steps you took for future debugging purposes.
|
||||
|
||||
- name: 'Create Pull Request'
|
||||
if: "steps.validate_version.outputs.CONTINUE == 'true'"
|
||||
uses: 'peter-evans/create-pull-request@v6'
|
||||
with:
|
||||
token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
|
||||
@@ -118,6 +118,7 @@ jobs:
|
||||
ORIGINAL_RELEASE_VERSION: '${{ steps.patch_version.outputs.RELEASE_VERSION }}'
|
||||
ORIGINAL_RELEASE_TAG: '${{ steps.patch_version.outputs.RELEASE_TAG }}'
|
||||
ORIGINAL_PREVIOUS_TAG: '${{ steps.patch_version.outputs.PREVIOUS_TAG }}'
|
||||
VARS_CLI_PACKAGE_NAME: ${{vars.CLI_PACKAGE_NAME}}
|
||||
run: |
|
||||
echo "🔍 Verifying no concurrent patch releases have occurred..."
|
||||
|
||||
@@ -129,7 +130,7 @@ jobs:
|
||||
|
||||
# Re-run the same version calculation script
|
||||
echo "Re-calculating version to check for changes..."
|
||||
CURRENT_PATCH_JSON=$(node scripts/get-release-version.js --cli-package-name="${{vars.CLI_PACKAGE_NAME}}" --type=patch --patch-from="${CHANNEL}")
|
||||
CURRENT_PATCH_JSON=$(node scripts/get-release-version.js --cli-package-name="${VARS_CLI_PACKAGE_NAME}" --type=patch --patch-from="${CHANNEL}")
|
||||
CURRENT_RELEASE_VERSION=$(echo "${CURRENT_PATCH_JSON}" | jq -r .releaseVersion)
|
||||
CURRENT_RELEASE_TAG=$(echo "${CURRENT_PATCH_JSON}" | jq -r .releaseTag)
|
||||
CURRENT_PREVIOUS_TAG=$(echo "${CURRENT_PATCH_JSON}" | jq -r .previousReleaseTag)
|
||||
@@ -162,10 +163,15 @@ jobs:
|
||||
- name: 'Print Calculated Version'
|
||||
run: |-
|
||||
echo "Patch Release Summary:"
|
||||
echo " Release Version: ${{ steps.patch_version.outputs.RELEASE_VERSION }}"
|
||||
echo " Release Tag: ${{ steps.patch_version.outputs.RELEASE_TAG }}"
|
||||
echo " NPM Tag: ${{ steps.patch_version.outputs.NPM_TAG }}"
|
||||
echo " Previous Tag: ${{ steps.patch_version.outputs.PREVIOUS_TAG }}"
|
||||
echo " Release Version: ${STEPS_PATCH_VERSION_OUTPUTS_RELEASE_VERSION}"
|
||||
echo " Release Tag: ${STEPS_PATCH_VERSION_OUTPUTS_RELEASE_TAG}"
|
||||
echo " NPM Tag: ${STEPS_PATCH_VERSION_OUTPUTS_NPM_TAG}"
|
||||
echo " Previous Tag: ${STEPS_PATCH_VERSION_OUTPUTS_PREVIOUS_TAG}"
|
||||
env:
|
||||
STEPS_PATCH_VERSION_OUTPUTS_RELEASE_VERSION: ${{ steps.patch_version.outputs.RELEASE_VERSION }}
|
||||
STEPS_PATCH_VERSION_OUTPUTS_RELEASE_TAG: ${{ steps.patch_version.outputs.RELEASE_TAG }}
|
||||
STEPS_PATCH_VERSION_OUTPUTS_NPM_TAG: ${{ steps.patch_version.outputs.NPM_TAG }}
|
||||
STEPS_PATCH_VERSION_OUTPUTS_PREVIOUS_TAG: ${{ steps.patch_version.outputs.PREVIOUS_TAG }}
|
||||
|
||||
- name: 'Run Tests'
|
||||
if: "${{github.event.inputs.force_skip_tests != 'true'}}"
|
||||
|
||||
@@ -362,23 +362,28 @@ jobs:
|
||||
- name: 'Create and switch to a new branch'
|
||||
id: 'release_branch'
|
||||
run: |
|
||||
BRANCH_NAME="chore/nightly-version-bump-${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}"
|
||||
BRANCH_NAME="chore/nightly-version-bump-${NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION}"
|
||||
git switch -c "${BRANCH_NAME}"
|
||||
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
|
||||
env:
|
||||
NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION: ${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}
|
||||
|
||||
- name: 'Update package versions'
|
||||
run: 'npm run release:version "${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}"'
|
||||
run: 'npm run release:version "${NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION}"'
|
||||
env:
|
||||
NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION: ${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}
|
||||
|
||||
- name: 'Commit and Push package versions'
|
||||
env:
|
||||
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
DRY_RUN: '${{ github.event.inputs.dry_run }}'
|
||||
NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION: ${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}
|
||||
run: |-
|
||||
git add package.json packages/*/package.json
|
||||
if [ -f package-lock.json ]; then
|
||||
git add package-lock.json
|
||||
fi
|
||||
git commit -m "chore(release): bump version to ${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}"
|
||||
git commit -m "chore(release): bump version to ${NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION}"
|
||||
if [[ "${DRY_RUN}" == "false" ]]; then
|
||||
echo "Pushing release branch to remote..."
|
||||
git push --set-upstream origin "${BRANCH_NAME}"
|
||||
|
||||
@@ -23,8 +23,8 @@ jobs:
|
||||
HEAD_SHA: '${{ github.event.inputs.head_sha || github.event.pull_request.head.sha }}'
|
||||
run: |
|
||||
mkdir -p ./pr
|
||||
echo '${{ env.REPO_NAME }}' > ./pr/repo_name
|
||||
echo '${{ env.HEAD_SHA }}' > ./pr/head_sha
|
||||
echo '${REPO_NAME}' > ./pr/repo_name
|
||||
echo '${HEAD_SHA}' > ./pr/head_sha
|
||||
- uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'repo_name'
|
||||
|
||||
@@ -61,4 +61,4 @@ gemini-debug.log
|
||||
.genkit
|
||||
.gemini-clipboard/
|
||||
.eslintcache
|
||||
evals/logs/
|
||||
evals/logs/
|
||||
@@ -18,6 +18,28 @@ on GitHub.
|
||||
| [Preview](preview.md) | Experimental features ready for early feedback. |
|
||||
| [Stable](latest.md) | Stable, recommended for general use. |
|
||||
|
||||
## Announcements: v0.30.0 - 2026-02-25
|
||||
|
||||
- **SDK & Custom Skills:** Introduced the initial SDK package, enabling dynamic
|
||||
system instructions, `SessionContext` for SDK tool calls, and support for
|
||||
custom skills
|
||||
([#18861](https://github.com/google-gemini/gemini-cli/pull/18861) by
|
||||
@mbleigh).
|
||||
- **Policy Engine Enhancements:** Added a new `--policy` flag for user-defined
|
||||
policies, introduced strict seatbelt profiles, and deprecated
|
||||
`--allowed-tools` in favor of the policy engine
|
||||
([#18500](https://github.com/google-gemini/gemini-cli/pull/18500) by
|
||||
@allenhutchison).
|
||||
- **UI & Themes:** Added a generic searchable list for settings and extensions,
|
||||
new Solarized themes, text wrapping for markdown tables, and a clean UI toggle
|
||||
prototype ([#19064](https://github.com/google-gemini/gemini-cli/pull/19064) by
|
||||
@rmedranollamas).
|
||||
- **Vim & Terminal Interaction:** Improved Vim support to feel more complete and
|
||||
added support for Ctrl-Z terminal suspension
|
||||
([#18755](https://github.com/google-gemini/gemini-cli/pull/18755) by
|
||||
@ppgranger, [#18931](https://github.com/google-gemini/gemini-cli/pull/18931)
|
||||
by @scidomino).
|
||||
|
||||
## Announcements: v0.29.0 - 2026-02-17
|
||||
|
||||
- **Plan Mode:** A new comprehensive planning capability with `/plan`,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Latest stable release: v0.29.0
|
||||
# Latest stable release: v0.30.0
|
||||
|
||||
Released: February 17, 2026
|
||||
Released: February 25, 2026
|
||||
|
||||
For most users, our latest stable release is the recommended release. Install
|
||||
the latest stable version with:
|
||||
@@ -11,371 +11,323 @@ npm install -g @google/gemini-cli
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Plan Mode:** Introduce a dedicated "Plan Mode" to help you architect complex
|
||||
changes before implementation. Use `/plan` to get started.
|
||||
- **Gemini 3 by Default:** Gemini 3 is now the default model family, bringing
|
||||
improved performance and reasoning capabilities to all users without needing a
|
||||
feature flag.
|
||||
- **Extension Discovery:** Easily discover and install extensions with the new
|
||||
exploration features and registry client.
|
||||
- **Enhanced Admin Controls:** New administrative capabilities allow for
|
||||
allowlisting MCP server configurations, giving organizations more control over
|
||||
available tools.
|
||||
- **Sub-agent Improvements:** Sub-agents have been transitioned to a new format
|
||||
with improved definitions and system prompts for better reliability.
|
||||
- **SDK & Custom Skills**: Introduced the initial SDK package, dynamic system
|
||||
instructions, `SessionContext` for SDK tool calls, and support for custom
|
||||
skills.
|
||||
- **Policy Engine Enhancements**: Added a `--policy` flag for user-defined
|
||||
policies, strict seatbelt profiles, and transitioned away from
|
||||
`--allowed-tools`.
|
||||
- **UI & Themes**: Introduced a generic searchable list for settings and
|
||||
extensions, added Solarized Dark and Light themes, text wrapping capabilities
|
||||
to markdown tables, and a clean UI toggle prototype.
|
||||
- **Vim Support & Ctrl-Z**: Improved Vim support to provide a more complete
|
||||
experience and added support for Ctrl-Z suspension.
|
||||
- **Plan Mode & Tools**: Plan Mode now supports project exploration without
|
||||
planning and skills can be enabled in plan mode. Tool output masking is
|
||||
enabled by default, and core tool definitions have been centralized.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- fix: remove `ask_user` tool from non-interactive modes by @jackwotherspoon in
|
||||
[#18154](https://github.com/google-gemini/gemini-cli/pull/18154)
|
||||
- fix(cli): allow restricted .env loading in untrusted sandboxed folders by
|
||||
@galz10 in [#17806](https://github.com/google-gemini/gemini-cli/pull/17806)
|
||||
- Encourage agent to utilize ecosystem tools to perform work by @gundermanc in
|
||||
[#17881](https://github.com/google-gemini/gemini-cli/pull/17881)
|
||||
- feat(plan): unify workflow location in system prompt to optimize caching by
|
||||
@jerop in [#18258](https://github.com/google-gemini/gemini-cli/pull/18258)
|
||||
- feat(core): enable getUserTierName in config by @sehoon38 in
|
||||
[#18265](https://github.com/google-gemini/gemini-cli/pull/18265)
|
||||
- feat(core): add default execution limits for subagents by @abhipatel12 in
|
||||
[#18274](https://github.com/google-gemini/gemini-cli/pull/18274)
|
||||
- Fix issue where agent gets stuck at interactive commands. by @gundermanc in
|
||||
[#18272](https://github.com/google-gemini/gemini-cli/pull/18272)
|
||||
- chore(release): bump version to 0.29.0-nightly.20260203.71f46f116 by
|
||||
- feat(ux): added text wrapping capabilities to markdown tables by @devr0306 in
|
||||
[#18240](https://github.com/google-gemini/gemini-cli/pull/18240)
|
||||
- Revert "fix(mcp): ensure MCP transport is closed to prevent memory leaks" by
|
||||
@skeshive in [#18771](https://github.com/google-gemini/gemini-cli/pull/18771)
|
||||
- chore(release): bump version to 0.30.0-nightly.20260210.a2174751d by
|
||||
@gemini-cli-robot in
|
||||
[#18243](https://github.com/google-gemini/gemini-cli/pull/18243)
|
||||
- feat(core): remove hardcoded policy bypass for local subagents by @abhipatel12
|
||||
in [#18153](https://github.com/google-gemini/gemini-cli/pull/18153)
|
||||
- feat(plan): implement `plan` slash command by @Adib234 in
|
||||
[#17698](https://github.com/google-gemini/gemini-cli/pull/17698)
|
||||
- feat: increase `ask_user` label limit to 16 characters by @jackwotherspoon in
|
||||
[#18320](https://github.com/google-gemini/gemini-cli/pull/18320)
|
||||
- Add information about the agent skills lifecycle and clarify docs-writer skill
|
||||
metadata. by @g-samroberts in
|
||||
[#18234](https://github.com/google-gemini/gemini-cli/pull/18234)
|
||||
- feat(core): add `enter_plan_mode` tool by @jerop in
|
||||
[#18324](https://github.com/google-gemini/gemini-cli/pull/18324)
|
||||
- Stop showing an error message in `/plan` by @Adib234 in
|
||||
[#18333](https://github.com/google-gemini/gemini-cli/pull/18333)
|
||||
- fix(hooks): remove unnecessary logging for hook registration by @abhipatel12
|
||||
in [#18332](https://github.com/google-gemini/gemini-cli/pull/18332)
|
||||
- fix(mcp): ensure MCP transport is closed to prevent memory leaks by
|
||||
@cbcoutinho in
|
||||
[#18054](https://github.com/google-gemini/gemini-cli/pull/18054)
|
||||
- feat(skills): implement linking for agent skills by @MushuEE in
|
||||
[#18295](https://github.com/google-gemini/gemini-cli/pull/18295)
|
||||
- Changelogs for 0.27.0 and 0.28.0-preview0 by @g-samroberts in
|
||||
[#18336](https://github.com/google-gemini/gemini-cli/pull/18336)
|
||||
- chore: correct docs as skills and hooks are stable by @jackwotherspoon in
|
||||
[#18358](https://github.com/google-gemini/gemini-cli/pull/18358)
|
||||
- feat(admin): Implement admin allowlist for MCP server configurations by
|
||||
@skeshive in [#18311](https://github.com/google-gemini/gemini-cli/pull/18311)
|
||||
- fix(core): add retry logic for transient SSL/TLS errors (#17318) by @ppgranger
|
||||
in [#18310](https://github.com/google-gemini/gemini-cli/pull/18310)
|
||||
- Add support for /extensions config command by @chrstnb in
|
||||
[#17895](https://github.com/google-gemini/gemini-cli/pull/17895)
|
||||
- fix(core): handle non-compliant mcpbridge responses from Xcode 26.3 by
|
||||
@peterfriese in
|
||||
[#18376](https://github.com/google-gemini/gemini-cli/pull/18376)
|
||||
- feat(cli): Add W, B, E Vim motions and operator support by @ademuri in
|
||||
[#16209](https://github.com/google-gemini/gemini-cli/pull/16209)
|
||||
- fix: Windows Specific Agent Quality & System Prompt by @scidomino in
|
||||
[#18351](https://github.com/google-gemini/gemini-cli/pull/18351)
|
||||
- feat(plan): support `replace` tool in plan mode to edit plans by @jerop in
|
||||
[#18379](https://github.com/google-gemini/gemini-cli/pull/18379)
|
||||
- Improving memory tool instructions and eval testing by @alisa-alisa in
|
||||
[#18091](https://github.com/google-gemini/gemini-cli/pull/18091)
|
||||
- fix(cli): color extension link success message green by @MushuEE in
|
||||
[#18386](https://github.com/google-gemini/gemini-cli/pull/18386)
|
||||
- undo by @jacob314 in
|
||||
[#18147](https://github.com/google-gemini/gemini-cli/pull/18147)
|
||||
- feat(plan): add guidance on iterating on approved plans vs creating new plans
|
||||
by @jerop in [#18346](https://github.com/google-gemini/gemini-cli/pull/18346)
|
||||
- feat(plan): fix invalid tool calls in plan mode by @Adib234 in
|
||||
[#18352](https://github.com/google-gemini/gemini-cli/pull/18352)
|
||||
- feat(plan): integrate planning artifacts and tools into primary workflows by
|
||||
@jerop in [#18375](https://github.com/google-gemini/gemini-cli/pull/18375)
|
||||
- Fix permission check by @scidomino in
|
||||
[#18395](https://github.com/google-gemini/gemini-cli/pull/18395)
|
||||
- ux(polish) autocomplete in the input prompt by @jacob314 in
|
||||
[#18181](https://github.com/google-gemini/gemini-cli/pull/18181)
|
||||
- fix: resolve infinite loop when using 'Modify with external editor' by
|
||||
@ppgranger in [#17453](https://github.com/google-gemini/gemini-cli/pull/17453)
|
||||
- feat: expand verify-release to macOS and Windows by @yunaseoul in
|
||||
[#18145](https://github.com/google-gemini/gemini-cli/pull/18145)
|
||||
- feat(plan): implement support for MCP servers in Plan mode by @Adib234 in
|
||||
[#18229](https://github.com/google-gemini/gemini-cli/pull/18229)
|
||||
- chore: update folder trust error messaging by @galz10 in
|
||||
[#18402](https://github.com/google-gemini/gemini-cli/pull/18402)
|
||||
- feat(plan): create a metric for execution of plans generated in plan mode by
|
||||
@Adib234 in [#18236](https://github.com/google-gemini/gemini-cli/pull/18236)
|
||||
- perf(ui): optimize stripUnsafeCharacters with regex by @gsquared94 in
|
||||
[#18413](https://github.com/google-gemini/gemini-cli/pull/18413)
|
||||
- feat(context): implement observation masking for tool outputs by @abhipatel12
|
||||
in [#18389](https://github.com/google-gemini/gemini-cli/pull/18389)
|
||||
- feat(core,cli): implement session-linked tool output storage and cleanup by
|
||||
@abhipatel12 in
|
||||
[#18416](https://github.com/google-gemini/gemini-cli/pull/18416)
|
||||
- Shorten temp directory by @joshualitt in
|
||||
[#17901](https://github.com/google-gemini/gemini-cli/pull/17901)
|
||||
- feat(plan): add behavioral evals for plan mode by @jerop in
|
||||
[#18437](https://github.com/google-gemini/gemini-cli/pull/18437)
|
||||
- Add extension registry client by @chrstnb in
|
||||
[#18396](https://github.com/google-gemini/gemini-cli/pull/18396)
|
||||
- Enable extension config by default by @chrstnb in
|
||||
[#18447](https://github.com/google-gemini/gemini-cli/pull/18447)
|
||||
- Automatically generate change logs on release by @g-samroberts in
|
||||
[#18401](https://github.com/google-gemini/gemini-cli/pull/18401)
|
||||
- Remove previewFeatures and default to Gemini 3 by @sehoon38 in
|
||||
[#18414](https://github.com/google-gemini/gemini-cli/pull/18414)
|
||||
- feat(admin): apply MCP allowlist to extensions & gemini mcp list command by
|
||||
@skeshive in [#18442](https://github.com/google-gemini/gemini-cli/pull/18442)
|
||||
- fix(cli): improve focus navigation for interactive and background shells by
|
||||
@galz10 in [#18343](https://github.com/google-gemini/gemini-cli/pull/18343)
|
||||
- Add shortcuts hint and panel for discoverability by @LyalinDotCom in
|
||||
[#18035](https://github.com/google-gemini/gemini-cli/pull/18035)
|
||||
- fix(config): treat system settings as read-only during migration and warn user
|
||||
by @spencer426 in
|
||||
[#18277](https://github.com/google-gemini/gemini-cli/pull/18277)
|
||||
- feat(plan): add positive test case and update eval stability policy by @jerop
|
||||
in [#18457](https://github.com/google-gemini/gemini-cli/pull/18457)
|
||||
- fix- windows: add shell: true for spawnSync to fix EINVAL with .cmd editors by
|
||||
@zackoch in [#18408](https://github.com/google-gemini/gemini-cli/pull/18408)
|
||||
- bug(core): Fix bug when saving plans. by @joshualitt in
|
||||
[#18465](https://github.com/google-gemini/gemini-cli/pull/18465)
|
||||
- Refactor atCommandProcessor by @scidomino in
|
||||
[#18461](https://github.com/google-gemini/gemini-cli/pull/18461)
|
||||
- feat(core): implement persistence and resumption for masked tool outputs by
|
||||
@abhipatel12 in
|
||||
[#18451](https://github.com/google-gemini/gemini-cli/pull/18451)
|
||||
- refactor: simplify tool output truncation to single config by @SandyTao520 in
|
||||
[#18446](https://github.com/google-gemini/gemini-cli/pull/18446)
|
||||
- bug(core): Ensure storage is initialized early, even if config is not. by
|
||||
@joshualitt in
|
||||
[#18471](https://github.com/google-gemini/gemini-cli/pull/18471)
|
||||
- chore: Update build-and-start script to support argument forwarding by
|
||||
@Abhijit-2592 in
|
||||
[#18241](https://github.com/google-gemini/gemini-cli/pull/18241)
|
||||
- fix(core): prevent subagent bypass in plan mode by @jerop in
|
||||
[#18484](https://github.com/google-gemini/gemini-cli/pull/18484)
|
||||
- feat(cli): add WebSocket-based network logging and streaming chunk support by
|
||||
@SandyTao520 in
|
||||
[#18383](https://github.com/google-gemini/gemini-cli/pull/18383)
|
||||
- feat(cli): update approval modes UI by @jerop in
|
||||
[#18476](https://github.com/google-gemini/gemini-cli/pull/18476)
|
||||
- fix(cli): reload skills and agents on extension restart by @NTaylorMullen in
|
||||
[#18411](https://github.com/google-gemini/gemini-cli/pull/18411)
|
||||
- fix(core): expand excludeTools with legacy aliases for renamed tools by
|
||||
@SandyTao520 in
|
||||
[#18498](https://github.com/google-gemini/gemini-cli/pull/18498)
|
||||
- feat(core): overhaul system prompt for rigor, integrity, and intent alignment
|
||||
by @NTaylorMullen in
|
||||
[#17263](https://github.com/google-gemini/gemini-cli/pull/17263)
|
||||
- Patch for generate changelog docs yaml file by @g-samroberts in
|
||||
[#18496](https://github.com/google-gemini/gemini-cli/pull/18496)
|
||||
- Code review fixes for show question mark pr. by @jacob314 in
|
||||
[#18480](https://github.com/google-gemini/gemini-cli/pull/18480)
|
||||
- fix(cli): add SS3 Shift+Tab support for Windows terminals by @ThanhNguyxn in
|
||||
[#18187](https://github.com/google-gemini/gemini-cli/pull/18187)
|
||||
- chore: remove redundant planning prompt from final shell by @jerop in
|
||||
[#18528](https://github.com/google-gemini/gemini-cli/pull/18528)
|
||||
- docs: require pr-creator skill for PR generation by @NTaylorMullen in
|
||||
[#18536](https://github.com/google-gemini/gemini-cli/pull/18536)
|
||||
- chore: update colors for ask_user dialog by @jackwotherspoon in
|
||||
[#18543](https://github.com/google-gemini/gemini-cli/pull/18543)
|
||||
- feat(core): exempt high-signal tools from output masking by @abhipatel12 in
|
||||
[#18545](https://github.com/google-gemini/gemini-cli/pull/18545)
|
||||
- refactor(core): remove memory tool instructions from Gemini 3 prompt by
|
||||
@NTaylorMullen in
|
||||
[#18559](https://github.com/google-gemini/gemini-cli/pull/18559)
|
||||
- chore: remove feedback instruction from system prompt by @NTaylorMullen in
|
||||
[#18560](https://github.com/google-gemini/gemini-cli/pull/18560)
|
||||
- feat(context): add remote configuration for tool output masking thresholds by
|
||||
@abhipatel12 in
|
||||
[#18553](https://github.com/google-gemini/gemini-cli/pull/18553)
|
||||
- feat(core): pause agent timeout budget while waiting for tool confirmation by
|
||||
@abhipatel12 in
|
||||
[#18415](https://github.com/google-gemini/gemini-cli/pull/18415)
|
||||
- refactor(config): remove experimental.enableEventDrivenScheduler setting by
|
||||
@abhipatel12 in
|
||||
[#17924](https://github.com/google-gemini/gemini-cli/pull/17924)
|
||||
- feat(cli): truncate shell output in UI history and improve active shell
|
||||
display by @jwhelangoog in
|
||||
[#17438](https://github.com/google-gemini/gemini-cli/pull/17438)
|
||||
- refactor(cli): switch useToolScheduler to event-driven engine by @abhipatel12
|
||||
in [#18565](https://github.com/google-gemini/gemini-cli/pull/18565)
|
||||
- fix(core): correct escaped interpolation in system prompt by @NTaylorMullen in
|
||||
[#18557](https://github.com/google-gemini/gemini-cli/pull/18557)
|
||||
- propagate abortSignal by @scidomino in
|
||||
[#18477](https://github.com/google-gemini/gemini-cli/pull/18477)
|
||||
- feat(core): conditionally include ctrl+f prompt based on interactive shell
|
||||
setting by @NTaylorMullen in
|
||||
[#18561](https://github.com/google-gemini/gemini-cli/pull/18561)
|
||||
- fix(core): ensure `enter_plan_mode` tool registration respects
|
||||
`experimental.plan` by @jerop in
|
||||
[#18587](https://github.com/google-gemini/gemini-cli/pull/18587)
|
||||
- feat(core): transition sub-agents to XML format and improve definitions by
|
||||
@NTaylorMullen in
|
||||
[#18555](https://github.com/google-gemini/gemini-cli/pull/18555)
|
||||
- docs: Add Plan Mode documentation by @jerop in
|
||||
[#18582](https://github.com/google-gemini/gemini-cli/pull/18582)
|
||||
- chore: strengthen validation guidance in system prompt by @NTaylorMullen in
|
||||
[#18544](https://github.com/google-gemini/gemini-cli/pull/18544)
|
||||
- Fix newline insertion bug in replace tool by @werdnum in
|
||||
[#18595](https://github.com/google-gemini/gemini-cli/pull/18595)
|
||||
- fix(evals): update save_memory evals and simplify tool description by
|
||||
@NTaylorMullen in
|
||||
[#18610](https://github.com/google-gemini/gemini-cli/pull/18610)
|
||||
- chore(evals): update validation_fidelity_pre_existing_errors to USUALLY_PASSES
|
||||
by @NTaylorMullen in
|
||||
[#18617](https://github.com/google-gemini/gemini-cli/pull/18617)
|
||||
- fix: shorten tool call IDs and fix duplicate tool name in truncated output
|
||||
filenames by @SandyTao520 in
|
||||
[#18600](https://github.com/google-gemini/gemini-cli/pull/18600)
|
||||
- feat(cli): implement atomic writes and safety checks for trusted folders by
|
||||
@galz10 in [#18406](https://github.com/google-gemini/gemini-cli/pull/18406)
|
||||
- Remove relative docs links by @chrstnb in
|
||||
[#18650](https://github.com/google-gemini/gemini-cli/pull/18650)
|
||||
- docs: add legacy snippets convention to GEMINI.md by @NTaylorMullen in
|
||||
[#18597](https://github.com/google-gemini/gemini-cli/pull/18597)
|
||||
- fix(chore): Support linting for cjs by @aswinashok44 in
|
||||
[#18639](https://github.com/google-gemini/gemini-cli/pull/18639)
|
||||
- feat: move shell efficiency guidelines to tool description by @NTaylorMullen
|
||||
in [#18614](https://github.com/google-gemini/gemini-cli/pull/18614)
|
||||
- Added "" as default value, since getText() used to expect a string only and
|
||||
thus crashed when undefined... Fixes #18076 by @019-Abhi in
|
||||
[#18099](https://github.com/google-gemini/gemini-cli/pull/18099)
|
||||
- Allow @-includes outside of workspaces (with permission) by @scidomino in
|
||||
[#18470](https://github.com/google-gemini/gemini-cli/pull/18470)
|
||||
- chore: make `ask_user` header description more clear by @jackwotherspoon in
|
||||
[#18657](https://github.com/google-gemini/gemini-cli/pull/18657)
|
||||
- refactor(core): model-dependent tool definitions by @aishaneeshah in
|
||||
[#18563](https://github.com/google-gemini/gemini-cli/pull/18563)
|
||||
- Harded code assist converter. by @jacob314 in
|
||||
[#18656](https://github.com/google-gemini/gemini-cli/pull/18656)
|
||||
- bug(core): Fix minor bug in migration logic. by @joshualitt in
|
||||
[#18661](https://github.com/google-gemini/gemini-cli/pull/18661)
|
||||
- feat: enable plan mode experiment in settings by @jerop in
|
||||
[#18636](https://github.com/google-gemini/gemini-cli/pull/18636)
|
||||
- refactor: push isValidPath() into parsePastedPaths() by @scidomino in
|
||||
[#18664](https://github.com/google-gemini/gemini-cli/pull/18664)
|
||||
- fix(cli): correct 'esc to cancel' position and restore duration display by
|
||||
@NTaylorMullen in
|
||||
[#18534](https://github.com/google-gemini/gemini-cli/pull/18534)
|
||||
- feat(cli): add DevTools integration with gemini-cli-devtools by @SandyTao520
|
||||
in [#18648](https://github.com/google-gemini/gemini-cli/pull/18648)
|
||||
- chore: remove unused exports and redundant hook files by @SandyTao520 in
|
||||
[#18681](https://github.com/google-gemini/gemini-cli/pull/18681)
|
||||
- Fix number of lines being reported in rewind confirmation dialog by @Adib234
|
||||
in [#18675](https://github.com/google-gemini/gemini-cli/pull/18675)
|
||||
- feat(cli): disable folder trust in headless mode by @galz10 in
|
||||
[#18407](https://github.com/google-gemini/gemini-cli/pull/18407)
|
||||
- Disallow unsafe type assertions by @gundermanc in
|
||||
[#18688](https://github.com/google-gemini/gemini-cli/pull/18688)
|
||||
- Change event type for release by @g-samroberts in
|
||||
[#18693](https://github.com/google-gemini/gemini-cli/pull/18693)
|
||||
- feat: handle multiple dynamic context filenames in system prompt by
|
||||
@NTaylorMullen in
|
||||
[#18598](https://github.com/google-gemini/gemini-cli/pull/18598)
|
||||
- Properly parse at-commands with narrow non-breaking spaces by @scidomino in
|
||||
[#18677](https://github.com/google-gemini/gemini-cli/pull/18677)
|
||||
- refactor(core): centralize core tool definitions and support model-specific
|
||||
schemas by @aishaneeshah in
|
||||
[#18662](https://github.com/google-gemini/gemini-cli/pull/18662)
|
||||
- feat(core): Render memory hierarchically in context. by @joshualitt in
|
||||
[#18350](https://github.com/google-gemini/gemini-cli/pull/18350)
|
||||
- feat: Ctrl+O to expand paste placeholder by @jackwotherspoon in
|
||||
[#18103](https://github.com/google-gemini/gemini-cli/pull/18103)
|
||||
- fix(cli): Improve header spacing by @NTaylorMullen in
|
||||
[#18531](https://github.com/google-gemini/gemini-cli/pull/18531)
|
||||
- Feature/quota visibility 16795 by @spencer426 in
|
||||
[#18203](https://github.com/google-gemini/gemini-cli/pull/18203)
|
||||
- Inline thinking bubbles with summary/full modes by @LyalinDotCom in
|
||||
[#18033](https://github.com/google-gemini/gemini-cli/pull/18033)
|
||||
- docs: remove TOC marker from Plan Mode header by @jerop in
|
||||
[#18678](https://github.com/google-gemini/gemini-cli/pull/18678)
|
||||
- fix(ui): remove redundant newlines in Gemini messages by @NTaylorMullen in
|
||||
[#18538](https://github.com/google-gemini/gemini-cli/pull/18538)
|
||||
- test(cli): fix AppContainer act() warnings and improve waitFor resilience by
|
||||
@NTaylorMullen in
|
||||
[#18676](https://github.com/google-gemini/gemini-cli/pull/18676)
|
||||
- refactor(core): refine Security & System Integrity section in system prompt by
|
||||
@NTaylorMullen in
|
||||
[#18601](https://github.com/google-gemini/gemini-cli/pull/18601)
|
||||
- Fix layout rounding. by @gundermanc in
|
||||
[#18667](https://github.com/google-gemini/gemini-cli/pull/18667)
|
||||
- docs(skills): enhance pr-creator safety and interactivity by @NTaylorMullen in
|
||||
[#18616](https://github.com/google-gemini/gemini-cli/pull/18616)
|
||||
- test(core): remove hardcoded model from TestRig by @NTaylorMullen in
|
||||
[#18710](https://github.com/google-gemini/gemini-cli/pull/18710)
|
||||
- feat(core): optimize sub-agents system prompt intro by @NTaylorMullen in
|
||||
[#18608](https://github.com/google-gemini/gemini-cli/pull/18608)
|
||||
- feat(cli): update approval mode labels and shortcuts per latest UX spec by
|
||||
@jerop in [#18698](https://github.com/google-gemini/gemini-cli/pull/18698)
|
||||
- fix(plan): update persistent approval mode setting by @Adib234 in
|
||||
[#18638](https://github.com/google-gemini/gemini-cli/pull/18638)
|
||||
- fix: move toasts location to left side by @jackwotherspoon in
|
||||
[#18705](https://github.com/google-gemini/gemini-cli/pull/18705)
|
||||
- feat(routing): restrict numerical routing to Gemini 3 family by @mattKorwel in
|
||||
[#18478](https://github.com/google-gemini/gemini-cli/pull/18478)
|
||||
- fix(ide): fix ide nudge setting by @skeshive in
|
||||
[#18733](https://github.com/google-gemini/gemini-cli/pull/18733)
|
||||
- fix(core): standardize tool formatting in system prompts by @NTaylorMullen in
|
||||
[#18615](https://github.com/google-gemini/gemini-cli/pull/18615)
|
||||
- chore: consolidate to green in ask user dialog by @jackwotherspoon in
|
||||
[#18734](https://github.com/google-gemini/gemini-cli/pull/18734)
|
||||
- feat: add `extensionsExplore` setting to enable extensions explore UI. by
|
||||
@sripasg in [#18686](https://github.com/google-gemini/gemini-cli/pull/18686)
|
||||
- feat(cli): defer devtools startup and integrate with F12 by @SandyTao520 in
|
||||
[#18695](https://github.com/google-gemini/gemini-cli/pull/18695)
|
||||
- ui: update & subdue footer colors and animate progress indicator by
|
||||
@keithguerin in
|
||||
[#18570](https://github.com/google-gemini/gemini-cli/pull/18570)
|
||||
- test: add model-specific snapshots for coreTools by @aishaneeshah in
|
||||
[#18707](https://github.com/google-gemini/gemini-cli/pull/18707)
|
||||
- ci: shard windows tests and fix event listener leaks by @NTaylorMullen in
|
||||
[#18670](https://github.com/google-gemini/gemini-cli/pull/18670)
|
||||
- fix: allow `ask_user` tool in yolo mode by @jackwotherspoon in
|
||||
[#18541](https://github.com/google-gemini/gemini-cli/pull/18541)
|
||||
- feat: redact disabled tools from system prompt (#13597) by @NTaylorMullen in
|
||||
[#18613](https://github.com/google-gemini/gemini-cli/pull/18613)
|
||||
- Update Gemini.md to use the curent year on creating new files by @sehoon38 in
|
||||
[#18460](https://github.com/google-gemini/gemini-cli/pull/18460)
|
||||
- Code review cleanup for thinking display by @jacob314 in
|
||||
[#18720](https://github.com/google-gemini/gemini-cli/pull/18720)
|
||||
- fix(cli): hide scrollbars when in alternate buffer copy mode by @werdnum in
|
||||
[#18354](https://github.com/google-gemini/gemini-cli/pull/18354)
|
||||
- Fix issues with rip grep by @gundermanc in
|
||||
[#18756](https://github.com/google-gemini/gemini-cli/pull/18756)
|
||||
- fix(cli): fix history navigation regression after prompt autocomplete by
|
||||
@sehoon38 in [#18752](https://github.com/google-gemini/gemini-cli/pull/18752)
|
||||
- chore: cleanup unused and add unlisted dependencies in packages/cli by
|
||||
[#18772](https://github.com/google-gemini/gemini-cli/pull/18772)
|
||||
- chore: cleanup unused and add unlisted dependencies in packages/core by
|
||||
@adamfweidman in
|
||||
[#18749](https://github.com/google-gemini/gemini-cli/pull/18749)
|
||||
- Fix issue where Gemini CLI creates tests in a new file by @gundermanc in
|
||||
[#18409](https://github.com/google-gemini/gemini-cli/pull/18409)
|
||||
- feat(telemetry): Ensure experiment IDs are included in OpenTelemetry logs by
|
||||
[#18762](https://github.com/google-gemini/gemini-cli/pull/18762)
|
||||
- chore(core): update activate_skill prompt verbiage to be more direct by
|
||||
@NTaylorMullen in
|
||||
[#18605](https://github.com/google-gemini/gemini-cli/pull/18605)
|
||||
- Add autoconfigure memory usage setting to the dialog by @jacob314 in
|
||||
[#18510](https://github.com/google-gemini/gemini-cli/pull/18510)
|
||||
- fix(core): prevent race condition in policy persistence by @braddux in
|
||||
[#18506](https://github.com/google-gemini/gemini-cli/pull/18506)
|
||||
- fix(evals): prevent false positive in hierarchical memory test by
|
||||
@Abhijit-2592 in
|
||||
[#18777](https://github.com/google-gemini/gemini-cli/pull/18777)
|
||||
- test(evals): mark all `save_memory` evals as `USUALLY_PASSES` due to
|
||||
unreliability by @jerop in
|
||||
[#18786](https://github.com/google-gemini/gemini-cli/pull/18786)
|
||||
- feat(cli): add setting to hide shortcuts hint UI by @LyalinDotCom in
|
||||
[#18562](https://github.com/google-gemini/gemini-cli/pull/18562)
|
||||
- feat(core): formalize 5-phase sequential planning workflow by @jerop in
|
||||
[#18759](https://github.com/google-gemini/gemini-cli/pull/18759)
|
||||
- Introduce limits for search results. by @gundermanc in
|
||||
[#18767](https://github.com/google-gemini/gemini-cli/pull/18767)
|
||||
- fix(cli): allow closing debug console after auto-open via flicker by
|
||||
@SandyTao520 in
|
||||
[#18795](https://github.com/google-gemini/gemini-cli/pull/18795)
|
||||
- feat(masking): enable tool output masking by default by @abhipatel12 in
|
||||
[#18564](https://github.com/google-gemini/gemini-cli/pull/18564)
|
||||
- perf(ui): optimize table rendering by memoizing styled characters by @devr0306
|
||||
in [#18770](https://github.com/google-gemini/gemini-cli/pull/18770)
|
||||
- feat: multi-line text answers in ask-user tool by @jackwotherspoon in
|
||||
[#18741](https://github.com/google-gemini/gemini-cli/pull/18741)
|
||||
- perf(cli): truncate large debug logs and limit message history by @mattKorwel
|
||||
in [#18663](https://github.com/google-gemini/gemini-cli/pull/18663)
|
||||
- fix(core): complete MCP discovery when configured servers are skipped by
|
||||
@LyalinDotCom in
|
||||
[#18586](https://github.com/google-gemini/gemini-cli/pull/18586)
|
||||
- fix(core): cache CLI version to ensure consistency during sessions by
|
||||
@sehoon38 in [#18793](https://github.com/google-gemini/gemini-cli/pull/18793)
|
||||
- fix(cli): resolve double rendering in shpool and address vscode lint warnings
|
||||
by @braddux in
|
||||
[#18704](https://github.com/google-gemini/gemini-cli/pull/18704)
|
||||
- feat(plan): document and validate Plan Mode policy overrides by @jerop in
|
||||
[#18825](https://github.com/google-gemini/gemini-cli/pull/18825)
|
||||
- Fix pressing any key to exit select mode. by @jacob314 in
|
||||
[#18421](https://github.com/google-gemini/gemini-cli/pull/18421)
|
||||
- fix(cli): update F12 behavior to only open drawer if browser fails by
|
||||
@SandyTao520 in
|
||||
[#18829](https://github.com/google-gemini/gemini-cli/pull/18829)
|
||||
- feat(plan): allow skills to be enabled in plan mode by @Adib234 in
|
||||
[#18817](https://github.com/google-gemini/gemini-cli/pull/18817)
|
||||
- docs(plan): add documentation for plan mode tools by @jerop in
|
||||
[#18827](https://github.com/google-gemini/gemini-cli/pull/18827)
|
||||
- Remove experimental note in extension settings docs by @chrstnb in
|
||||
[#18822](https://github.com/google-gemini/gemini-cli/pull/18822)
|
||||
- Update prompt and grep tool definition to limit context size by @gundermanc in
|
||||
[#18780](https://github.com/google-gemini/gemini-cli/pull/18780)
|
||||
- docs(plan): add `ask_user` tool documentation by @jerop in
|
||||
[#18830](https://github.com/google-gemini/gemini-cli/pull/18830)
|
||||
- Revert unintended credentials exposure by @Adib234 in
|
||||
[#18840](https://github.com/google-gemini/gemini-cli/pull/18840)
|
||||
- feat(core): update internal utility models to Gemini 3 by @SandyTao520 in
|
||||
[#18773](https://github.com/google-gemini/gemini-cli/pull/18773)
|
||||
- feat(a2a): add value-resolver for auth credential resolution by @adamfweidman
|
||||
in [#18653](https://github.com/google-gemini/gemini-cli/pull/18653)
|
||||
- Removed getPlainTextLength by @devr0306 in
|
||||
[#18848](https://github.com/google-gemini/gemini-cli/pull/18848)
|
||||
- More grep prompt tweaks by @gundermanc in
|
||||
[#18846](https://github.com/google-gemini/gemini-cli/pull/18846)
|
||||
- refactor(cli): Reactive useSettingsStore hook by @psinha40898 in
|
||||
[#14915](https://github.com/google-gemini/gemini-cli/pull/14915)
|
||||
- fix(mcp): Ensure that stdio MCP server execution has the `GEMINI_CLI=1` env
|
||||
variable populated. by @richieforeman in
|
||||
[#18832](https://github.com/google-gemini/gemini-cli/pull/18832)
|
||||
- fix(core): improve headless mode detection for flags and query args by @galz10
|
||||
in [#18855](https://github.com/google-gemini/gemini-cli/pull/18855)
|
||||
- refactor(cli): simplify UI and remove legacy inline tool confirmation logic by
|
||||
@abhipatel12 in
|
||||
[#18566](https://github.com/google-gemini/gemini-cli/pull/18566)
|
||||
- feat(cli): deprecate --allowed-tools and excludeTools in favor of policy
|
||||
engine by @Abhijit-2592 in
|
||||
[#18508](https://github.com/google-gemini/gemini-cli/pull/18508)
|
||||
- fix(workflows): improve maintainer detection for automated PR actions by
|
||||
@bdmorgan in [#18869](https://github.com/google-gemini/gemini-cli/pull/18869)
|
||||
- refactor(cli): consolidate useToolScheduler and delete legacy implementation
|
||||
by @abhipatel12 in
|
||||
[#18567](https://github.com/google-gemini/gemini-cli/pull/18567)
|
||||
- Update changelog for v0.28.0 and v0.29.0-preview0 by @g-samroberts in
|
||||
[#18819](https://github.com/google-gemini/gemini-cli/pull/18819)
|
||||
- fix(core): ensure sub-agents are registered regardless of tools.allowed by
|
||||
@mattKorwel in
|
||||
[#18870](https://github.com/google-gemini/gemini-cli/pull/18870)
|
||||
- Show notification when there's a conflict with an extensions command by
|
||||
@chrstnb in [#17890](https://github.com/google-gemini/gemini-cli/pull/17890)
|
||||
- fix(cli): dismiss '?' shortcuts help on hotkeys and active states by
|
||||
@LyalinDotCom in
|
||||
[#18583](https://github.com/google-gemini/gemini-cli/pull/18583)
|
||||
- fix(core): prioritize conditional policy rules and harden Plan Mode by
|
||||
@Abhijit-2592 in
|
||||
[#18882](https://github.com/google-gemini/gemini-cli/pull/18882)
|
||||
- feat(core): refine Plan Mode system prompt for agentic execution by
|
||||
@NTaylorMullen in
|
||||
[#18799](https://github.com/google-gemini/gemini-cli/pull/18799)
|
||||
- feat(plan): create metrics for usage of `AskUser` tool by @Adib234 in
|
||||
[#18820](https://github.com/google-gemini/gemini-cli/pull/18820)
|
||||
- feat(cli): support Ctrl-Z suspension by @scidomino in
|
||||
[#18931](https://github.com/google-gemini/gemini-cli/pull/18931)
|
||||
- fix(github-actions): use robot PAT for release creation to trigger release
|
||||
notes by @SandyTao520 in
|
||||
[#18794](https://github.com/google-gemini/gemini-cli/pull/18794)
|
||||
- feat: add strict seatbelt profiles and remove unusable closed profiles by
|
||||
@SandyTao520 in
|
||||
[#18876](https://github.com/google-gemini/gemini-cli/pull/18876)
|
||||
- chore: cleanup unused and add unlisted dependencies in packages/a2a-server by
|
||||
@adamfweidman in
|
||||
[#18916](https://github.com/google-gemini/gemini-cli/pull/18916)
|
||||
- fix(plan): isolate plan files per session by @Adib234 in
|
||||
[#18757](https://github.com/google-gemini/gemini-cli/pull/18757)
|
||||
- fix: character truncation in raw markdown mode by @jackwotherspoon in
|
||||
[#18938](https://github.com/google-gemini/gemini-cli/pull/18938)
|
||||
- feat(cli): prototype clean UI toggle and minimal-mode bleed-through by
|
||||
@LyalinDotCom in
|
||||
[#18683](https://github.com/google-gemini/gemini-cli/pull/18683)
|
||||
- ui(polish) blend background color with theme by @jacob314 in
|
||||
[#18802](https://github.com/google-gemini/gemini-cli/pull/18802)
|
||||
- Add generic searchable list to back settings and extensions by @chrstnb in
|
||||
[#18838](https://github.com/google-gemini/gemini-cli/pull/18838)
|
||||
- feat(ui): align `AskUser` color scheme with UX spec by @jerop in
|
||||
[#18943](https://github.com/google-gemini/gemini-cli/pull/18943)
|
||||
- Hide AskUser tool validation errors from UI (agent self-corrects) by @jerop in
|
||||
[#18954](https://github.com/google-gemini/gemini-cli/pull/18954)
|
||||
- bug(cli) fix flicker due to AppContainer continuous initialization by
|
||||
@jacob314 in [#18958](https://github.com/google-gemini/gemini-cli/pull/18958)
|
||||
- feat(admin): Add admin controls documentation by @skeshive in
|
||||
[#18644](https://github.com/google-gemini/gemini-cli/pull/18644)
|
||||
- feat(cli): disable ctrl-s shortcut outside of alternate buffer mode by
|
||||
@jacob314 in [#18887](https://github.com/google-gemini/gemini-cli/pull/18887)
|
||||
- fix(vim): vim support that feels (more) complete by @ppgranger in
|
||||
[#18755](https://github.com/google-gemini/gemini-cli/pull/18755)
|
||||
- feat(policy): add --policy flag for user defined policies by @allenhutchison
|
||||
in [#18500](https://github.com/google-gemini/gemini-cli/pull/18500)
|
||||
- Update installation guide by @g-samroberts in
|
||||
[#18823](https://github.com/google-gemini/gemini-cli/pull/18823)
|
||||
- refactor(core): centralize tool definitions (Group 1: replace, search, grep)
|
||||
by @aishaneeshah in
|
||||
[#18944](https://github.com/google-gemini/gemini-cli/pull/18944)
|
||||
- refactor(cli): finalize event-driven transition and remove interaction bridge
|
||||
by @abhipatel12 in
|
||||
[#18569](https://github.com/google-gemini/gemini-cli/pull/18569)
|
||||
- Fix drag and drop escaping by @scidomino in
|
||||
[#18965](https://github.com/google-gemini/gemini-cli/pull/18965)
|
||||
- feat(sdk): initial package bootstrap for SDK by @mbleigh in
|
||||
[#18861](https://github.com/google-gemini/gemini-cli/pull/18861)
|
||||
- feat(sdk): implements SessionContext for SDK tool calls by @mbleigh in
|
||||
[#18862](https://github.com/google-gemini/gemini-cli/pull/18862)
|
||||
- fix(plan): make question type required in AskUser tool by @Adib234 in
|
||||
[#18959](https://github.com/google-gemini/gemini-cli/pull/18959)
|
||||
- fix(core): ensure --yolo does not force headless mode by @NTaylorMullen in
|
||||
[#18976](https://github.com/google-gemini/gemini-cli/pull/18976)
|
||||
- refactor(core): adopt `CoreToolCallStatus` enum for type safety by @jerop in
|
||||
[#18998](https://github.com/google-gemini/gemini-cli/pull/18998)
|
||||
- Enable in-CLI extension management commands for team by @chrstnb in
|
||||
[#18957](https://github.com/google-gemini/gemini-cli/pull/18957)
|
||||
- Adjust lint rules to avoid unnecessary warning. by @scidomino in
|
||||
[#18970](https://github.com/google-gemini/gemini-cli/pull/18970)
|
||||
- fix(vscode): resolve unsafe type assertion lint errors by @ehedlund in
|
||||
[#19006](https://github.com/google-gemini/gemini-cli/pull/19006)
|
||||
- Remove unnecessary eslint config file by @scidomino in
|
||||
[#19015](https://github.com/google-gemini/gemini-cli/pull/19015)
|
||||
- fix(core): Prevent loop detection false positives on lists with long shared
|
||||
prefixes by @SandyTao520 in
|
||||
[#18975](https://github.com/google-gemini/gemini-cli/pull/18975)
|
||||
- feat(core): fallback to chat-base when using unrecognized models for chat by
|
||||
@SandyTao520 in
|
||||
[#19016](https://github.com/google-gemini/gemini-cli/pull/19016)
|
||||
- docs: fix inconsistent commandRegex example in policy engine by @NTaylorMullen
|
||||
in [#19027](https://github.com/google-gemini/gemini-cli/pull/19027)
|
||||
- fix(plan): persist the approval mode in UI even when agent is thinking by
|
||||
@Adib234 in [#18955](https://github.com/google-gemini/gemini-cli/pull/18955)
|
||||
- feat(sdk): Implement dynamic system instructions by @mbleigh in
|
||||
[#18863](https://github.com/google-gemini/gemini-cli/pull/18863)
|
||||
- Docs: Refresh docs to organize and standardize reference materials. by
|
||||
@jkcinouye in [#18403](https://github.com/google-gemini/gemini-cli/pull/18403)
|
||||
- fix windows escaping (and broken tests) by @scidomino in
|
||||
[#19011](https://github.com/google-gemini/gemini-cli/pull/19011)
|
||||
- refactor: use `CoreToolCallStatus` in the the history data model by @jerop in
|
||||
[#19033](https://github.com/google-gemini/gemini-cli/pull/19033)
|
||||
- feat(cleanup): enable 30-day session retention by default by @skeshive in
|
||||
[#18854](https://github.com/google-gemini/gemini-cli/pull/18854)
|
||||
- feat(plan): hide plan write and edit operations on plans in Plan Mode by
|
||||
@jerop in [#19012](https://github.com/google-gemini/gemini-cli/pull/19012)
|
||||
- bug(ui) fix flicker refreshing background color by @jacob314 in
|
||||
[#19041](https://github.com/google-gemini/gemini-cli/pull/19041)
|
||||
- chore: fix dep vulnerabilities by @scidomino in
|
||||
[#19036](https://github.com/google-gemini/gemini-cli/pull/19036)
|
||||
- Revamp automated changelog skill by @g-samroberts in
|
||||
[#18974](https://github.com/google-gemini/gemini-cli/pull/18974)
|
||||
- feat(sdk): implement support for custom skills by @mbleigh in
|
||||
[#19031](https://github.com/google-gemini/gemini-cli/pull/19031)
|
||||
- refactor(core): complete centralization of core tool definitions by
|
||||
@aishaneeshah in
|
||||
[#18991](https://github.com/google-gemini/gemini-cli/pull/18991)
|
||||
- feat: add /commands reload to refresh custom TOML commands by @korade-krushna
|
||||
in [#19078](https://github.com/google-gemini/gemini-cli/pull/19078)
|
||||
- fix(cli): wrap terminal capability queries in hidden sequence by @srithreepo
|
||||
in [#19080](https://github.com/google-gemini/gemini-cli/pull/19080)
|
||||
- fix(workflows): fix GitHub App token permissions for maintainer detection by
|
||||
@bdmorgan in [#19139](https://github.com/google-gemini/gemini-cli/pull/19139)
|
||||
- test: fix hook integration test flakiness on Windows CI by @NTaylorMullen in
|
||||
[#18665](https://github.com/google-gemini/gemini-cli/pull/18665)
|
||||
- fix(core): Encourage non-interactive flags for scaffolding commands by
|
||||
@NTaylorMullen in
|
||||
[#18804](https://github.com/google-gemini/gemini-cli/pull/18804)
|
||||
- fix(core): propagate User-Agent header to setup-phase CodeAssist API calls by
|
||||
@gsquared94 in
|
||||
[#19182](https://github.com/google-gemini/gemini-cli/pull/19182)
|
||||
- docs: document .agents/skills alias and discovery precedence by @kevmoo in
|
||||
[#19166](https://github.com/google-gemini/gemini-cli/pull/19166)
|
||||
- feat(cli): add loading state to new agents notification by @sehoon38 in
|
||||
[#19190](https://github.com/google-gemini/gemini-cli/pull/19190)
|
||||
- Add base branch to workflow. by @g-samroberts in
|
||||
[#19189](https://github.com/google-gemini/gemini-cli/pull/19189)
|
||||
- feat(cli): handle invalid model names in useQuotaAndFallback by @sehoon38 in
|
||||
[#19222](https://github.com/google-gemini/gemini-cli/pull/19222)
|
||||
- docs: custom themes in extensions by @jackwotherspoon in
|
||||
[#19219](https://github.com/google-gemini/gemini-cli/pull/19219)
|
||||
- Disable workspace settings when starting GCLI in the home directory. by
|
||||
@kevinjwang1 in
|
||||
[#19034](https://github.com/google-gemini/gemini-cli/pull/19034)
|
||||
- feat(cli): refactor model command to support set and manage subcommands by
|
||||
@sehoon38 in [#19221](https://github.com/google-gemini/gemini-cli/pull/19221)
|
||||
- Add refresh/reload aliases to slash command subcommands by @korade-krushna in
|
||||
[#19218](https://github.com/google-gemini/gemini-cli/pull/19218)
|
||||
- refactor: consolidate development rules and add cli guidelines by @jacob314 in
|
||||
[#19214](https://github.com/google-gemini/gemini-cli/pull/19214)
|
||||
- chore(ui): remove outdated tip about model routing by @sehoon38 in
|
||||
[#19226](https://github.com/google-gemini/gemini-cli/pull/19226)
|
||||
- feat(core): support custom reasoning models by default by @NTaylorMullen in
|
||||
[#19227](https://github.com/google-gemini/gemini-cli/pull/19227)
|
||||
- Add Solarized Dark and Solarized Light themes by @rmedranollamas in
|
||||
[#19064](https://github.com/google-gemini/gemini-cli/pull/19064)
|
||||
- fix(telemetry): replace JSON.stringify with safeJsonStringify in file
|
||||
exporters by @gsquared94 in
|
||||
[#19244](https://github.com/google-gemini/gemini-cli/pull/19244)
|
||||
- feat(telemetry): add keychain availability and token storage metrics by
|
||||
@abhipatel12 in
|
||||
[#18971](https://github.com/google-gemini/gemini-cli/pull/18971)
|
||||
- feat(cli): update approval mode cycle order by @jerop in
|
||||
[#19254](https://github.com/google-gemini/gemini-cli/pull/19254)
|
||||
- refactor(cli): code review cleanup fix for tab+tab by @jacob314 in
|
||||
[#18967](https://github.com/google-gemini/gemini-cli/pull/18967)
|
||||
- feat(plan): support project exploration without planning when in plan mode by
|
||||
@Adib234 in [#18992](https://github.com/google-gemini/gemini-cli/pull/18992)
|
||||
- feat: add role-specific statistics to telemetry and UI (cont. #15234) by
|
||||
@yunaseoul in [#18824](https://github.com/google-gemini/gemini-cli/pull/18824)
|
||||
- feat(cli): remove Plan Mode from rotation when actively working by @jerop in
|
||||
[#19262](https://github.com/google-gemini/gemini-cli/pull/19262)
|
||||
- Fix side breakage where anchors don't work in slugs. by @g-samroberts in
|
||||
[#19261](https://github.com/google-gemini/gemini-cli/pull/19261)
|
||||
- feat(config): add setting to make directory tree context configurable by
|
||||
@kevin-ramdass in
|
||||
[#18747](https://github.com/google-gemini/gemini-cli/pull/18747)
|
||||
- fix(patch): cherry-pick e9a9474 to release/v0.29.0-preview.0-pr-18840 to patch
|
||||
version v0.29.0-preview.0 and create version 0.29.0-preview.1 by
|
||||
[#19053](https://github.com/google-gemini/gemini-cli/pull/19053)
|
||||
- fix(acp): Wait for mcp initialization in acp (#18893) by @Mervap in
|
||||
[#18894](https://github.com/google-gemini/gemini-cli/pull/18894)
|
||||
- docs: format UTC times in releases doc by @pavan-sh in
|
||||
[#18169](https://github.com/google-gemini/gemini-cli/pull/18169)
|
||||
- Docs: Clarify extensions documentation. by @jkcinouye in
|
||||
[#19277](https://github.com/google-gemini/gemini-cli/pull/19277)
|
||||
- refactor(core): modularize tool definitions by model family by @aishaneeshah
|
||||
in [#19269](https://github.com/google-gemini/gemini-cli/pull/19269)
|
||||
- fix(paths): Add cross-platform path normalization by @spencer426 in
|
||||
[#18939](https://github.com/google-gemini/gemini-cli/pull/18939)
|
||||
- feat(core): experimental in-progress steering hints (1 of 3) by @joshualitt in
|
||||
[#19008](https://github.com/google-gemini/gemini-cli/pull/19008)
|
||||
- fix(patch): cherry-pick 261788c to release/v0.30.0-preview.0-pr-19453 to patch
|
||||
version v0.30.0-preview.0 and create version 0.30.0-preview.1 by
|
||||
@gemini-cli-robot in
|
||||
[#18841](https://github.com/google-gemini/gemini-cli/pull/18841)
|
||||
- fix(patch): cherry-pick 08e8eea to release/v0.29.0-preview.1-pr-18855 to patch
|
||||
version v0.29.0-preview.1 and create version 0.29.0-preview.2 by
|
||||
[#19490](https://github.com/google-gemini/gemini-cli/pull/19490)
|
||||
- fix(patch): cherry-pick c43500c to release/v0.30.0-preview.1-pr-19502 to patch
|
||||
version v0.30.0-preview.1 and create version 0.30.0-preview.2 by
|
||||
@gemini-cli-robot in
|
||||
[#18905](https://github.com/google-gemini/gemini-cli/pull/18905)
|
||||
- fix(patch): cherry-pick d0c6a56 to release/v0.29.0-preview.2-pr-18976 to patch
|
||||
version v0.29.0-preview.2 and create version 0.29.0-preview.3 by
|
||||
[#19521](https://github.com/google-gemini/gemini-cli/pull/19521)
|
||||
- fix(patch): cherry-pick aa9163d to release/v0.30.0-preview.3-pr-19991 to patch
|
||||
version v0.30.0-preview.3 and create version 0.30.0-preview.4 by
|
||||
@gemini-cli-robot in
|
||||
[#19023](https://github.com/google-gemini/gemini-cli/pull/19023)
|
||||
- fix(patch): cherry-pick e5ff202 to release/v0.29.0-preview.3-pr-19254 to patch
|
||||
version v0.29.0-preview.3 and create version 0.29.0-preview.4 by
|
||||
[#20040](https://github.com/google-gemini/gemini-cli/pull/20040)
|
||||
- fix(patch): cherry-pick 2c1d6f8 to release/v0.30.0-preview.4-pr-19369 to patch
|
||||
version v0.30.0-preview.4 and create version 0.30.0-preview.5 by
|
||||
@gemini-cli-robot in
|
||||
[#19264](https://github.com/google-gemini/gemini-cli/pull/19264)
|
||||
- fix(patch): cherry-pick 9590a09 to release/v0.29.0-preview.4-pr-18771 to patch
|
||||
version v0.29.0-preview.4 and create version 0.29.0-preview.5 by
|
||||
[#20086](https://github.com/google-gemini/gemini-cli/pull/20086)
|
||||
- fix(patch): cherry-pick d96bd05 to release/v0.30.0-preview.5-pr-19867 to patch
|
||||
version v0.30.0-preview.5 and create version 0.30.0-preview.6 by
|
||||
@gemini-cli-robot in
|
||||
[#19274](https://github.com/google-gemini/gemini-cli/pull/19274)
|
||||
[#20112](https://github.com/google-gemini/gemini-cli/pull/20112)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.28.2...v0.29.0
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.29.7...v0.30.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Preview release: v0.30.0-preview.5
|
||||
# Preview release: v0.31.0-preview.0
|
||||
|
||||
Released: February 24, 2026
|
||||
Released: February 25, 2026
|
||||
|
||||
Our preview release includes the latest, new, and experimental features. This
|
||||
release may not be as stable as our [latest weekly release](latest.md).
|
||||
@@ -13,306 +13,400 @@ npm install -g @google/gemini-cli@preview
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Initial SDK Package:** Introduced the initial SDK package with support for
|
||||
custom skills and dynamic system instructions.
|
||||
- **Refined Plan Mode:** Refined Plan Mode with support for enabling skills,
|
||||
improved agentic execution, and project exploration without planning.
|
||||
- **Enhanced CLI UI:** Enhanced CLI UI with a new clean UI toggle, minimal-mode
|
||||
bleed-through, and support for Ctrl-Z suspension.
|
||||
- **`--policy` flag:** Added the `--policy` flag to support user-defined
|
||||
policies.
|
||||
- **New Themes:** Added Solarized Dark and Solarized Light themes.
|
||||
- **Plan Mode Enhancements**: Numerous additions including automatic model
|
||||
switching, custom storage directory configuration, message injection upon
|
||||
manual exit, enforcement of read-only constraints, and centralized tool
|
||||
visibility in the policy engine.
|
||||
- **Policy Engine Updates**: Project-level policy support added, alongside MCP
|
||||
server wildcard support, tool annotation propagation and matching, and
|
||||
workspace-level "Always Allow" persistence.
|
||||
- **MCP Integration Improvements**: Better integration through support for MCP
|
||||
progress updates with input validation and throttling, environment variable
|
||||
expansion for servers, and full details expansion on tool approval.
|
||||
- **CLI & Core UX Enhancements**: Several UI and quality-of-life updates such as
|
||||
Alt+D for forward word deletion, macOS run-event notifications, enhanced
|
||||
folder trust configurations with security warnings, improved startup warnings,
|
||||
and a new experimental browser agent.
|
||||
- **Security & Stability**: Introduced the Conseca framework, deceptive URL and
|
||||
Unicode character detection, stricter access checks, rate limits on web fetch,
|
||||
and resolved multiple dependency vulnerabilities.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- fix(patch): cherry-pick 2c1d6f8 to release/v0.30.0-preview.4-pr-19369 to patch
|
||||
version v0.30.0-preview.4 and create version 0.30.0-preview.5 by
|
||||
@gemini-cli-robot in
|
||||
[#20086](https://github.com/google-gemini/gemini-cli/pull/20086)
|
||||
- fix(patch): cherry-pick 261788c to release/v0.30.0-preview.0-pr-19453 to patch
|
||||
version v0.30.0-preview.0 and create version 0.30.0-preview.1 by
|
||||
@gemini-cli-robot in
|
||||
[#19490](https://github.com/google-gemini/gemini-cli/pull/19490)
|
||||
- feat(ux): added text wrapping capabilities to markdown tables by @devr0306 in
|
||||
[#18240](https://github.com/google-gemini/gemini-cli/pull/18240)
|
||||
- Revert "fix(mcp): ensure MCP transport is closed to prevent memory leaks" by
|
||||
@skeshive in [#18771](https://github.com/google-gemini/gemini-cli/pull/18771)
|
||||
- chore(release): bump version to 0.30.0-nightly.20260210.a2174751d by
|
||||
@gemini-cli-robot in
|
||||
[#18772](https://github.com/google-gemini/gemini-cli/pull/18772)
|
||||
- chore: cleanup unused and add unlisted dependencies in packages/core by
|
||||
@adamfweidman in
|
||||
[#18762](https://github.com/google-gemini/gemini-cli/pull/18762)
|
||||
- chore(core): update activate_skill prompt verbiage to be more direct by
|
||||
@NTaylorMullen in
|
||||
[#18605](https://github.com/google-gemini/gemini-cli/pull/18605)
|
||||
- Add autoconfigure memory usage setting to the dialog by @jacob314 in
|
||||
[#18510](https://github.com/google-gemini/gemini-cli/pull/18510)
|
||||
- fix(core): prevent race condition in policy persistence by @braddux in
|
||||
[#18506](https://github.com/google-gemini/gemini-cli/pull/18506)
|
||||
- fix(evals): prevent false positive in hierarchical memory test by
|
||||
@Abhijit-2592 in
|
||||
[#18777](https://github.com/google-gemini/gemini-cli/pull/18777)
|
||||
- test(evals): mark all `save_memory` evals as `USUALLY_PASSES` due to
|
||||
unreliability by @jerop in
|
||||
[#18786](https://github.com/google-gemini/gemini-cli/pull/18786)
|
||||
- feat(cli): add setting to hide shortcuts hint UI by @LyalinDotCom in
|
||||
[#18562](https://github.com/google-gemini/gemini-cli/pull/18562)
|
||||
- feat(core): formalize 5-phase sequential planning workflow by @jerop in
|
||||
[#18759](https://github.com/google-gemini/gemini-cli/pull/18759)
|
||||
- Introduce limits for search results. by @gundermanc in
|
||||
[#18767](https://github.com/google-gemini/gemini-cli/pull/18767)
|
||||
- fix(cli): allow closing debug console after auto-open via flicker by
|
||||
- Use ranged reads and limited searches and fuzzy editing improvements by
|
||||
@gundermanc in
|
||||
[#19240](https://github.com/google-gemini/gemini-cli/pull/19240)
|
||||
- Fix bottom border color by @jacob314 in
|
||||
[#19266](https://github.com/google-gemini/gemini-cli/pull/19266)
|
||||
- Release note generator fix by @g-samroberts in
|
||||
[#19363](https://github.com/google-gemini/gemini-cli/pull/19363)
|
||||
- test(evals): add behavioral tests for tool output masking by @NTaylorMullen in
|
||||
[#19172](https://github.com/google-gemini/gemini-cli/pull/19172)
|
||||
- docs: clarify preflight instructions in GEMINI.md by @NTaylorMullen in
|
||||
[#19377](https://github.com/google-gemini/gemini-cli/pull/19377)
|
||||
- feat(cli): add gemini --resume hint on exit by @Mag1ck in
|
||||
[#16285](https://github.com/google-gemini/gemini-cli/pull/16285)
|
||||
- fix: optimize height calculations for ask_user dialog by @jackwotherspoon in
|
||||
[#19017](https://github.com/google-gemini/gemini-cli/pull/19017)
|
||||
- feat(cli): add Alt+D for forward word deletion by @scidomino in
|
||||
[#19300](https://github.com/google-gemini/gemini-cli/pull/19300)
|
||||
- Disable failing eval test by @chrstnb in
|
||||
[#19455](https://github.com/google-gemini/gemini-cli/pull/19455)
|
||||
- fix(cli): support legacy onConfirm callback in ToolActionsContext by
|
||||
@SandyTao520 in
|
||||
[#18795](https://github.com/google-gemini/gemini-cli/pull/18795)
|
||||
- feat(masking): enable tool output masking by default by @abhipatel12 in
|
||||
[#18564](https://github.com/google-gemini/gemini-cli/pull/18564)
|
||||
- perf(ui): optimize table rendering by memoizing styled characters by @devr0306
|
||||
in [#18770](https://github.com/google-gemini/gemini-cli/pull/18770)
|
||||
- feat: multi-line text answers in ask-user tool by @jackwotherspoon in
|
||||
[#18741](https://github.com/google-gemini/gemini-cli/pull/18741)
|
||||
- perf(cli): truncate large debug logs and limit message history by @mattKorwel
|
||||
in [#18663](https://github.com/google-gemini/gemini-cli/pull/18663)
|
||||
- fix(core): complete MCP discovery when configured servers are skipped by
|
||||
[#19369](https://github.com/google-gemini/gemini-cli/pull/19369)
|
||||
- chore(deps): bump tar from 7.5.7 to 7.5.8 by dependabot[bot] in
|
||||
[#19367](https://github.com/google-gemini/gemini-cli/pull/19367)
|
||||
- fix(plan): allow safe fallback when experiment setting for plan is not enabled
|
||||
but approval mode at startup is plan by @Adib234 in
|
||||
[#19439](https://github.com/google-gemini/gemini-cli/pull/19439)
|
||||
- Add explicit color-convert dependency by @chrstnb in
|
||||
[#19460](https://github.com/google-gemini/gemini-cli/pull/19460)
|
||||
- feat(devtools): migrate devtools package into monorepo by @SandyTao520 in
|
||||
[#18936](https://github.com/google-gemini/gemini-cli/pull/18936)
|
||||
- fix(core): clarify plan mode constraints and exit mechanism by @jerop in
|
||||
[#19438](https://github.com/google-gemini/gemini-cli/pull/19438)
|
||||
- feat(cli): add macOS run-event notifications (interactive only) by
|
||||
@LyalinDotCom in
|
||||
[#18586](https://github.com/google-gemini/gemini-cli/pull/18586)
|
||||
- fix(core): cache CLI version to ensure consistency during sessions by
|
||||
@sehoon38 in [#18793](https://github.com/google-gemini/gemini-cli/pull/18793)
|
||||
- fix(cli): resolve double rendering in shpool and address vscode lint warnings
|
||||
by @braddux in
|
||||
[#18704](https://github.com/google-gemini/gemini-cli/pull/18704)
|
||||
- feat(plan): document and validate Plan Mode policy overrides by @jerop in
|
||||
[#18825](https://github.com/google-gemini/gemini-cli/pull/18825)
|
||||
- Fix pressing any key to exit select mode. by @jacob314 in
|
||||
[#18421](https://github.com/google-gemini/gemini-cli/pull/18421)
|
||||
- fix(cli): update F12 behavior to only open drawer if browser fails by
|
||||
[#19056](https://github.com/google-gemini/gemini-cli/pull/19056)
|
||||
- Changelog for v0.29.0 by @gemini-cli-robot in
|
||||
[#19361](https://github.com/google-gemini/gemini-cli/pull/19361)
|
||||
- fix(ui): preventing empty history items from being added by @devr0306 in
|
||||
[#19014](https://github.com/google-gemini/gemini-cli/pull/19014)
|
||||
- Changelog for v0.30.0-preview.0 by @gemini-cli-robot in
|
||||
[#19364](https://github.com/google-gemini/gemini-cli/pull/19364)
|
||||
- feat(core): add support for MCP progress updates by @NTaylorMullen in
|
||||
[#19046](https://github.com/google-gemini/gemini-cli/pull/19046)
|
||||
- fix(core): ensure directory exists before writing conversation file by
|
||||
@godwiniheuwa in
|
||||
[#18429](https://github.com/google-gemini/gemini-cli/pull/18429)
|
||||
- fix(ui): move margin from top to bottom in ToolGroupMessage by @imadraude in
|
||||
[#17198](https://github.com/google-gemini/gemini-cli/pull/17198)
|
||||
- fix(cli): treat unknown slash commands as regular input instead of showing
|
||||
error by @skyvanguard in
|
||||
[#17393](https://github.com/google-gemini/gemini-cli/pull/17393)
|
||||
- feat(core): experimental in-progress steering hints (2 of 2) by @joshualitt in
|
||||
[#19307](https://github.com/google-gemini/gemini-cli/pull/19307)
|
||||
- docs(plan): add documentation for plan mode command by @Adib234 in
|
||||
[#19467](https://github.com/google-gemini/gemini-cli/pull/19467)
|
||||
- fix(core): ripgrep fails when pattern looks like ripgrep flag by @syvb in
|
||||
[#18858](https://github.com/google-gemini/gemini-cli/pull/18858)
|
||||
- fix(cli): disable auto-completion on Shift+Tab to preserve mode cycling by
|
||||
@NTaylorMullen in
|
||||
[#19451](https://github.com/google-gemini/gemini-cli/pull/19451)
|
||||
- use issuer instead of authorization_endpoint for oauth discovery by
|
||||
@garrettsparks in
|
||||
[#17332](https://github.com/google-gemini/gemini-cli/pull/17332)
|
||||
- feat(cli): include `/dir add` directories in @ autocomplete suggestions by
|
||||
@jasmeetsb in [#19246](https://github.com/google-gemini/gemini-cli/pull/19246)
|
||||
- feat(admin): Admin settings should only apply if adminControlsApplicable =
|
||||
true and fetch errors should be fatal by @skeshive in
|
||||
[#19453](https://github.com/google-gemini/gemini-cli/pull/19453)
|
||||
- Format strict-development-rules command by @g-samroberts in
|
||||
[#19484](https://github.com/google-gemini/gemini-cli/pull/19484)
|
||||
- feat(core): centralize compatibility checks and add TrueColor detection by
|
||||
@spencer426 in
|
||||
[#19478](https://github.com/google-gemini/gemini-cli/pull/19478)
|
||||
- Remove unused files and update index and sidebar. by @g-samroberts in
|
||||
[#19479](https://github.com/google-gemini/gemini-cli/pull/19479)
|
||||
- Migrate core render util to use xterm.js as part of the rendering loop. by
|
||||
@jacob314 in [#19044](https://github.com/google-gemini/gemini-cli/pull/19044)
|
||||
- Changelog for v0.30.0-preview.1 by @gemini-cli-robot in
|
||||
[#19496](https://github.com/google-gemini/gemini-cli/pull/19496)
|
||||
- build: replace deprecated built-in punycode with userland package by @jacob314
|
||||
in [#19502](https://github.com/google-gemini/gemini-cli/pull/19502)
|
||||
- Speculative fixes to try to fix react error. by @jacob314 in
|
||||
[#19508](https://github.com/google-gemini/gemini-cli/pull/19508)
|
||||
- fix spacing by @jacob314 in
|
||||
[#19494](https://github.com/google-gemini/gemini-cli/pull/19494)
|
||||
- fix(core): ensure user rejections update tool outcome for telemetry by
|
||||
@abhiasap in [#18982](https://github.com/google-gemini/gemini-cli/pull/18982)
|
||||
- fix(acp): Initialize config (#18897) by @Mervap in
|
||||
[#18898](https://github.com/google-gemini/gemini-cli/pull/18898)
|
||||
- fix(core): add error logging for IDE fetch failures by @yuvrajangadsingh in
|
||||
[#17981](https://github.com/google-gemini/gemini-cli/pull/17981)
|
||||
- feat(acp): support set_mode interface (#18890) by @Mervap in
|
||||
[#18891](https://github.com/google-gemini/gemini-cli/pull/18891)
|
||||
- fix(core): robust workspace-based IDE connection discovery by @ehedlund in
|
||||
[#18443](https://github.com/google-gemini/gemini-cli/pull/18443)
|
||||
- Deflake windows tests. by @jacob314 in
|
||||
[#19511](https://github.com/google-gemini/gemini-cli/pull/19511)
|
||||
- Fix: Avoid tool confirmation timeout when no UI listeners are present by
|
||||
@pdHaku0 in [#17955](https://github.com/google-gemini/gemini-cli/pull/17955)
|
||||
- format md file by @scidomino in
|
||||
[#19474](https://github.com/google-gemini/gemini-cli/pull/19474)
|
||||
- feat(cli): add experimental.useOSC52Copy setting by @scidomino in
|
||||
[#19488](https://github.com/google-gemini/gemini-cli/pull/19488)
|
||||
- feat(cli): replace loading phrases boolean with enum setting by @LyalinDotCom
|
||||
in [#19347](https://github.com/google-gemini/gemini-cli/pull/19347)
|
||||
- Update skill to adjust for generated results. by @g-samroberts in
|
||||
[#19500](https://github.com/google-gemini/gemini-cli/pull/19500)
|
||||
- Fix message too large issue. by @gundermanc in
|
||||
[#19499](https://github.com/google-gemini/gemini-cli/pull/19499)
|
||||
- fix(core): prevent duplicate tool approval entries in auto-saved.toml by
|
||||
@Abhijit-2592 in
|
||||
[#19487](https://github.com/google-gemini/gemini-cli/pull/19487)
|
||||
- fix(core): resolve crash in ClearcutLogger when os.cpus() is empty by @Adib234
|
||||
in [#19555](https://github.com/google-gemini/gemini-cli/pull/19555)
|
||||
- chore(core): improve encapsulation and remove unused exports by @adamfweidman
|
||||
in [#19556](https://github.com/google-gemini/gemini-cli/pull/19556)
|
||||
- Revert "Add generic searchable list to back settings and extensions (… by
|
||||
@chrstnb in [#19434](https://github.com/google-gemini/gemini-cli/pull/19434)
|
||||
- fix(core): improve error type extraction for telemetry by @yunaseoul in
|
||||
[#19565](https://github.com/google-gemini/gemini-cli/pull/19565)
|
||||
- fix: remove extra padding in Composer by @jackwotherspoon in
|
||||
[#19529](https://github.com/google-gemini/gemini-cli/pull/19529)
|
||||
- feat(plan): support configuring custom plans storage directory by @jerop in
|
||||
[#19577](https://github.com/google-gemini/gemini-cli/pull/19577)
|
||||
- Migrate files to resource or references folder. by @g-samroberts in
|
||||
[#19503](https://github.com/google-gemini/gemini-cli/pull/19503)
|
||||
- feat(policy): implement project-level policy support by @Abhijit-2592 in
|
||||
[#18682](https://github.com/google-gemini/gemini-cli/pull/18682)
|
||||
- feat(core): Implement parallel FC for read only tools. by @joshualitt in
|
||||
[#18791](https://github.com/google-gemini/gemini-cli/pull/18791)
|
||||
- chore(skills): adds pr-address-comments skill to work on PR feedback by
|
||||
@mbleigh in [#19576](https://github.com/google-gemini/gemini-cli/pull/19576)
|
||||
- refactor(sdk): introduce session-based architecture by @mbleigh in
|
||||
[#19180](https://github.com/google-gemini/gemini-cli/pull/19180)
|
||||
- fix(ci): add fallback JSON extraction to issue triage workflow by @bdmorgan in
|
||||
[#19593](https://github.com/google-gemini/gemini-cli/pull/19593)
|
||||
- feat(core): refine Edit and WriteFile tool schemas for Gemini 3 by
|
||||
@SandyTao520 in
|
||||
[#18829](https://github.com/google-gemini/gemini-cli/pull/18829)
|
||||
- feat(plan): allow skills to be enabled in plan mode by @Adib234 in
|
||||
[#18817](https://github.com/google-gemini/gemini-cli/pull/18817)
|
||||
- docs(plan): add documentation for plan mode tools by @jerop in
|
||||
[#18827](https://github.com/google-gemini/gemini-cli/pull/18827)
|
||||
- Remove experimental note in extension settings docs by @chrstnb in
|
||||
[#18822](https://github.com/google-gemini/gemini-cli/pull/18822)
|
||||
- Update prompt and grep tool definition to limit context size by @gundermanc in
|
||||
[#18780](https://github.com/google-gemini/gemini-cli/pull/18780)
|
||||
- docs(plan): add `ask_user` tool documentation by @jerop in
|
||||
[#18830](https://github.com/google-gemini/gemini-cli/pull/18830)
|
||||
- Revert unintended credentials exposure by @Adib234 in
|
||||
[#18840](https://github.com/google-gemini/gemini-cli/pull/18840)
|
||||
- feat(core): update internal utility models to Gemini 3 by @SandyTao520 in
|
||||
[#18773](https://github.com/google-gemini/gemini-cli/pull/18773)
|
||||
- feat(a2a): add value-resolver for auth credential resolution by @adamfweidman
|
||||
in [#18653](https://github.com/google-gemini/gemini-cli/pull/18653)
|
||||
- Removed getPlainTextLength by @devr0306 in
|
||||
[#18848](https://github.com/google-gemini/gemini-cli/pull/18848)
|
||||
- More grep prompt tweaks by @gundermanc in
|
||||
[#18846](https://github.com/google-gemini/gemini-cli/pull/18846)
|
||||
- refactor(cli): Reactive useSettingsStore hook by @psinha40898 in
|
||||
[#14915](https://github.com/google-gemini/gemini-cli/pull/14915)
|
||||
- fix(mcp): Ensure that stdio MCP server execution has the `GEMINI_CLI=1` env
|
||||
variable populated. by @richieforeman in
|
||||
[#18832](https://github.com/google-gemini/gemini-cli/pull/18832)
|
||||
- fix(core): improve headless mode detection for flags and query args by @galz10
|
||||
in [#18855](https://github.com/google-gemini/gemini-cli/pull/18855)
|
||||
- refactor(cli): simplify UI and remove legacy inline tool confirmation logic by
|
||||
@abhipatel12 in
|
||||
[#18566](https://github.com/google-gemini/gemini-cli/pull/18566)
|
||||
- feat(cli): deprecate --allowed-tools and excludeTools in favor of policy
|
||||
engine by @Abhijit-2592 in
|
||||
[#18508](https://github.com/google-gemini/gemini-cli/pull/18508)
|
||||
- fix(workflows): improve maintainer detection for automated PR actions by
|
||||
@bdmorgan in [#18869](https://github.com/google-gemini/gemini-cli/pull/18869)
|
||||
- refactor(cli): consolidate useToolScheduler and delete legacy implementation
|
||||
by @abhipatel12 in
|
||||
[#18567](https://github.com/google-gemini/gemini-cli/pull/18567)
|
||||
- Update changelog for v0.28.0 and v0.29.0-preview0 by @g-samroberts in
|
||||
[#18819](https://github.com/google-gemini/gemini-cli/pull/18819)
|
||||
- fix(core): ensure sub-agents are registered regardless of tools.allowed by
|
||||
[#19476](https://github.com/google-gemini/gemini-cli/pull/19476)
|
||||
- Changelog for v0.30.0-preview.3 by @gemini-cli-robot in
|
||||
[#19585](https://github.com/google-gemini/gemini-cli/pull/19585)
|
||||
- fix(plan): exclude EnterPlanMode tool from YOLO mode by @Adib234 in
|
||||
[#19570](https://github.com/google-gemini/gemini-cli/pull/19570)
|
||||
- chore: resolve build warnings and update dependencies by @mattKorwel in
|
||||
[#18880](https://github.com/google-gemini/gemini-cli/pull/18880)
|
||||
- feat(ui): add source indicators to slash commands by @ehedlund in
|
||||
[#18839](https://github.com/google-gemini/gemini-cli/pull/18839)
|
||||
- docs: refine Plan Mode documentation structure and workflow by @jerop in
|
||||
[#19644](https://github.com/google-gemini/gemini-cli/pull/19644)
|
||||
- Docs: Update release information regarding Gemini 3.1 by @jkcinouye in
|
||||
[#19568](https://github.com/google-gemini/gemini-cli/pull/19568)
|
||||
- fix(security): rate limit web_fetch tool to mitigate DDoS via prompt injection
|
||||
by @mattKorwel in
|
||||
[#19567](https://github.com/google-gemini/gemini-cli/pull/19567)
|
||||
- Add initial implementation of /extensions explore command by @chrstnb in
|
||||
[#19029](https://github.com/google-gemini/gemini-cli/pull/19029)
|
||||
- fix: use discoverOAuthFromWWWAuthenticate for reactive OAuth flow (#18760) by
|
||||
@maximus12793 in
|
||||
[#19038](https://github.com/google-gemini/gemini-cli/pull/19038)
|
||||
- Search updates by @alisa-alisa in
|
||||
[#19482](https://github.com/google-gemini/gemini-cli/pull/19482)
|
||||
- feat(cli): add support for numpad SS3 sequences by @scidomino in
|
||||
[#19659](https://github.com/google-gemini/gemini-cli/pull/19659)
|
||||
- feat(cli): enhance folder trust with configuration discovery and security
|
||||
warnings by @galz10 in
|
||||
[#19492](https://github.com/google-gemini/gemini-cli/pull/19492)
|
||||
- feat(ui): improve startup warnings UX with dismissal and show-count limits by
|
||||
@spencer426 in
|
||||
[#19584](https://github.com/google-gemini/gemini-cli/pull/19584)
|
||||
- feat(a2a): Add API key authentication provider by @adamfweidman in
|
||||
[#19548](https://github.com/google-gemini/gemini-cli/pull/19548)
|
||||
- Send accepted/removed lines with ACCEPT_FILE telemetry. by @gundermanc in
|
||||
[#19670](https://github.com/google-gemini/gemini-cli/pull/19670)
|
||||
- feat(models): support Gemini 3.1 Pro Preview and fixes by @sehoon38 in
|
||||
[#19676](https://github.com/google-gemini/gemini-cli/pull/19676)
|
||||
- feat(plan): enforce read-only constraints in Plan Mode by @mattKorwel in
|
||||
[#19433](https://github.com/google-gemini/gemini-cli/pull/19433)
|
||||
- fix(cli): allow perfect match @scripts/test-windows-paths.js completions to
|
||||
submit on Enter by @spencer426 in
|
||||
[#19562](https://github.com/google-gemini/gemini-cli/pull/19562)
|
||||
- fix(core): treat 503 Service Unavailable as retryable quota error by @sehoon38
|
||||
in [#19642](https://github.com/google-gemini/gemini-cli/pull/19642)
|
||||
- Update sidebar.json for to allow top nav tabs. by @g-samroberts in
|
||||
[#19595](https://github.com/google-gemini/gemini-cli/pull/19595)
|
||||
- security: strip deceptive Unicode characters from terminal output by @ehedlund
|
||||
in [#19026](https://github.com/google-gemini/gemini-cli/pull/19026)
|
||||
- Fixes 'input.on' is not a function error in Gemini CLI by @gundermanc in
|
||||
[#19691](https://github.com/google-gemini/gemini-cli/pull/19691)
|
||||
- Revert "feat(ui): add source indicators to slash commands" by @ehedlund in
|
||||
[#19695](https://github.com/google-gemini/gemini-cli/pull/19695)
|
||||
- security: implement deceptive URL detection and disclosure in tool
|
||||
confirmations by @ehedlund in
|
||||
[#19288](https://github.com/google-gemini/gemini-cli/pull/19288)
|
||||
- fix(core): restore auth consent in headless mode and add unit tests by
|
||||
@ehedlund in [#19689](https://github.com/google-gemini/gemini-cli/pull/19689)
|
||||
- Fix unsafe assertions in code_assist folder. by @gundermanc in
|
||||
[#19706](https://github.com/google-gemini/gemini-cli/pull/19706)
|
||||
- feat(cli): make JetBrains warning more specific by @jacob314 in
|
||||
[#19687](https://github.com/google-gemini/gemini-cli/pull/19687)
|
||||
- fix(cli): extensions dialog UX polish by @jacob314 in
|
||||
[#19685](https://github.com/google-gemini/gemini-cli/pull/19685)
|
||||
- fix(cli): use getDisplayString for manual model selection in dialog by
|
||||
@sehoon38 in [#19726](https://github.com/google-gemini/gemini-cli/pull/19726)
|
||||
- feat(policy): repurpose "Always Allow" persistence to workspace level by
|
||||
@Abhijit-2592 in
|
||||
[#19707](https://github.com/google-gemini/gemini-cli/pull/19707)
|
||||
- fix(cli): re-enable CLI banner by @sehoon38 in
|
||||
[#19741](https://github.com/google-gemini/gemini-cli/pull/19741)
|
||||
- Disallow and suppress unsafe assignment by @gundermanc in
|
||||
[#19736](https://github.com/google-gemini/gemini-cli/pull/19736)
|
||||
- feat(core): migrate read_file to 1-based start_line/end_line parameters by
|
||||
@adamfweidman in
|
||||
[#19526](https://github.com/google-gemini/gemini-cli/pull/19526)
|
||||
- feat(cli): improve CTRL+O experience for both standard and alternate screen
|
||||
buffer (ASB) modes by @jwhelangoog in
|
||||
[#19010](https://github.com/google-gemini/gemini-cli/pull/19010)
|
||||
- Utilize pipelining of grep_search -> read_file to eliminate turns by
|
||||
@gundermanc in
|
||||
[#19574](https://github.com/google-gemini/gemini-cli/pull/19574)
|
||||
- refactor(core): remove unsafe type assertions in error utils (Phase 1.1) by
|
||||
@mattKorwel in
|
||||
[#18870](https://github.com/google-gemini/gemini-cli/pull/18870)
|
||||
- Show notification when there's a conflict with an extensions command by
|
||||
@chrstnb in [#17890](https://github.com/google-gemini/gemini-cli/pull/17890)
|
||||
- fix(cli): dismiss '?' shortcuts help on hotkeys and active states by
|
||||
@LyalinDotCom in
|
||||
[#18583](https://github.com/google-gemini/gemini-cli/pull/18583)
|
||||
- fix(core): prioritize conditional policy rules and harden Plan Mode by
|
||||
@Abhijit-2592 in
|
||||
[#18882](https://github.com/google-gemini/gemini-cli/pull/18882)
|
||||
- feat(core): refine Plan Mode system prompt for agentic execution by
|
||||
[#19750](https://github.com/google-gemini/gemini-cli/pull/19750)
|
||||
- Disallow unsafe returns. by @gundermanc in
|
||||
[#19767](https://github.com/google-gemini/gemini-cli/pull/19767)
|
||||
- fix(cli): filter subagent sessions from resume history by @abhipatel12 in
|
||||
[#19698](https://github.com/google-gemini/gemini-cli/pull/19698)
|
||||
- chore(lint): fix lint errors seen when running npm run lint by @abhipatel12 in
|
||||
[#19844](https://github.com/google-gemini/gemini-cli/pull/19844)
|
||||
- feat(core): remove unnecessary login verbiage from Code Assist auth by
|
||||
@NTaylorMullen in
|
||||
[#18799](https://github.com/google-gemini/gemini-cli/pull/18799)
|
||||
- feat(plan): create metrics for usage of `AskUser` tool by @Adib234 in
|
||||
[#18820](https://github.com/google-gemini/gemini-cli/pull/18820)
|
||||
- feat(cli): support Ctrl-Z suspension by @scidomino in
|
||||
[#18931](https://github.com/google-gemini/gemini-cli/pull/18931)
|
||||
- fix(github-actions): use robot PAT for release creation to trigger release
|
||||
notes by @SandyTao520 in
|
||||
[#18794](https://github.com/google-gemini/gemini-cli/pull/18794)
|
||||
- feat: add strict seatbelt profiles and remove unusable closed profiles by
|
||||
[#19861](https://github.com/google-gemini/gemini-cli/pull/19861)
|
||||
- fix(plan): time share by approval mode dashboard reporting negative time
|
||||
shares by @Adib234 in
|
||||
[#19847](https://github.com/google-gemini/gemini-cli/pull/19847)
|
||||
- fix(core): allow any preview model in quota access check by @bdmorgan in
|
||||
[#19867](https://github.com/google-gemini/gemini-cli/pull/19867)
|
||||
- fix(core): prevent omission placeholder deletions in replace/write_file by
|
||||
@nsalerni in [#19870](https://github.com/google-gemini/gemini-cli/pull/19870)
|
||||
- fix(core): add uniqueness guard to edit tool by @Shivangisharma4 in
|
||||
[#19890](https://github.com/google-gemini/gemini-cli/pull/19890)
|
||||
- refactor(config): remove enablePromptCompletion from settings by @sehoon38 in
|
||||
[#19974](https://github.com/google-gemini/gemini-cli/pull/19974)
|
||||
- refactor(core): move session conversion logic to core by @abhipatel12 in
|
||||
[#19972](https://github.com/google-gemini/gemini-cli/pull/19972)
|
||||
- Fix: Persist manual model selection on restart #19864 by @Nixxx19 in
|
||||
[#19891](https://github.com/google-gemini/gemini-cli/pull/19891)
|
||||
- fix(core): increase default retry attempts and add quota error backoff by
|
||||
@sehoon38 in [#19949](https://github.com/google-gemini/gemini-cli/pull/19949)
|
||||
- feat(core): add policy chain support for Gemini 3.1 by @sehoon38 in
|
||||
[#19991](https://github.com/google-gemini/gemini-cli/pull/19991)
|
||||
- Updates command reference and /stats command. by @g-samroberts in
|
||||
[#19794](https://github.com/google-gemini/gemini-cli/pull/19794)
|
||||
- Fix for silent failures in non-interactive mode by @owenofbrien in
|
||||
[#19905](https://github.com/google-gemini/gemini-cli/pull/19905)
|
||||
- fix(plan): allow plan mode writes on Windows and fix prompt paths by @Adib234
|
||||
in [#19658](https://github.com/google-gemini/gemini-cli/pull/19658)
|
||||
- fix(core): prevent OAuth server crash on unexpected requests by @reyyanxahmed
|
||||
in [#19668](https://github.com/google-gemini/gemini-cli/pull/19668)
|
||||
- feat: Map tool kinds to explicit ACP.ToolKind values and update test … by
|
||||
@sripasg in [#19547](https://github.com/google-gemini/gemini-cli/pull/19547)
|
||||
- chore: restrict gemini-automted-issue-triage to only allow echo by @galz10 in
|
||||
[#20047](https://github.com/google-gemini/gemini-cli/pull/20047)
|
||||
- Allow ask headers longer than 16 chars by @scidomino in
|
||||
[#20041](https://github.com/google-gemini/gemini-cli/pull/20041)
|
||||
- fix(core): prevent state corruption in McpClientManager during collis by @h30s
|
||||
in [#19782](https://github.com/google-gemini/gemini-cli/pull/19782)
|
||||
- fix(bundling): copy devtools package to bundle for runtime resolution by
|
||||
@SandyTao520 in
|
||||
[#18876](https://github.com/google-gemini/gemini-cli/pull/18876)
|
||||
- chore: cleanup unused and add unlisted dependencies in packages/a2a-server by
|
||||
@adamfweidman in
|
||||
[#18916](https://github.com/google-gemini/gemini-cli/pull/18916)
|
||||
- fix(plan): isolate plan files per session by @Adib234 in
|
||||
[#18757](https://github.com/google-gemini/gemini-cli/pull/18757)
|
||||
- fix: character truncation in raw markdown mode by @jackwotherspoon in
|
||||
[#18938](https://github.com/google-gemini/gemini-cli/pull/18938)
|
||||
- feat(cli): prototype clean UI toggle and minimal-mode bleed-through by
|
||||
@LyalinDotCom in
|
||||
[#18683](https://github.com/google-gemini/gemini-cli/pull/18683)
|
||||
- ui(polish) blend background color with theme by @jacob314 in
|
||||
[#18802](https://github.com/google-gemini/gemini-cli/pull/18802)
|
||||
- Add generic searchable list to back settings and extensions by @chrstnb in
|
||||
[#18838](https://github.com/google-gemini/gemini-cli/pull/18838)
|
||||
- feat(ui): align `AskUser` color scheme with UX spec by @jerop in
|
||||
[#18943](https://github.com/google-gemini/gemini-cli/pull/18943)
|
||||
- Hide AskUser tool validation errors from UI (agent self-corrects) by @jerop in
|
||||
[#18954](https://github.com/google-gemini/gemini-cli/pull/18954)
|
||||
- bug(cli) fix flicker due to AppContainer continuous initialization by
|
||||
@jacob314 in [#18958](https://github.com/google-gemini/gemini-cli/pull/18958)
|
||||
- feat(admin): Add admin controls documentation by @skeshive in
|
||||
[#18644](https://github.com/google-gemini/gemini-cli/pull/18644)
|
||||
- feat(cli): disable ctrl-s shortcut outside of alternate buffer mode by
|
||||
@jacob314 in [#18887](https://github.com/google-gemini/gemini-cli/pull/18887)
|
||||
- fix(vim): vim support that feels (more) complete by @ppgranger in
|
||||
[#18755](https://github.com/google-gemini/gemini-cli/pull/18755)
|
||||
- feat(policy): add --policy flag for user defined policies by @allenhutchison
|
||||
in [#18500](https://github.com/google-gemini/gemini-cli/pull/18500)
|
||||
- Update installation guide by @g-samroberts in
|
||||
[#18823](https://github.com/google-gemini/gemini-cli/pull/18823)
|
||||
- refactor(core): centralize tool definitions (Group 1: replace, search, grep)
|
||||
by @aishaneeshah in
|
||||
[#18944](https://github.com/google-gemini/gemini-cli/pull/18944)
|
||||
- refactor(cli): finalize event-driven transition and remove interaction bridge
|
||||
by @abhipatel12 in
|
||||
[#18569](https://github.com/google-gemini/gemini-cli/pull/18569)
|
||||
- Fix drag and drop escaping by @scidomino in
|
||||
[#18965](https://github.com/google-gemini/gemini-cli/pull/18965)
|
||||
- feat(sdk): initial package bootstrap for SDK by @mbleigh in
|
||||
[#18861](https://github.com/google-gemini/gemini-cli/pull/18861)
|
||||
- feat(sdk): implements SessionContext for SDK tool calls by @mbleigh in
|
||||
[#18862](https://github.com/google-gemini/gemini-cli/pull/18862)
|
||||
- fix(plan): make question type required in AskUser tool by @Adib234 in
|
||||
[#18959](https://github.com/google-gemini/gemini-cli/pull/18959)
|
||||
- fix(core): ensure --yolo does not force headless mode by @NTaylorMullen in
|
||||
[#18976](https://github.com/google-gemini/gemini-cli/pull/18976)
|
||||
- refactor(core): adopt `CoreToolCallStatus` enum for type safety by @jerop in
|
||||
[#18998](https://github.com/google-gemini/gemini-cli/pull/18998)
|
||||
- Enable in-CLI extension management commands for team by @chrstnb in
|
||||
[#18957](https://github.com/google-gemini/gemini-cli/pull/18957)
|
||||
- Adjust lint rules to avoid unnecessary warning. by @scidomino in
|
||||
[#18970](https://github.com/google-gemini/gemini-cli/pull/18970)
|
||||
- fix(vscode): resolve unsafe type assertion lint errors by @ehedlund in
|
||||
[#19006](https://github.com/google-gemini/gemini-cli/pull/19006)
|
||||
- Remove unnecessary eslint config file by @scidomino in
|
||||
[#19015](https://github.com/google-gemini/gemini-cli/pull/19015)
|
||||
- fix(core): Prevent loop detection false positives on lists with long shared
|
||||
prefixes by @SandyTao520 in
|
||||
[#18975](https://github.com/google-gemini/gemini-cli/pull/18975)
|
||||
- feat(core): fallback to chat-base when using unrecognized models for chat by
|
||||
@SandyTao520 in
|
||||
[#19016](https://github.com/google-gemini/gemini-cli/pull/19016)
|
||||
- docs: fix inconsistent commandRegex example in policy engine by @NTaylorMullen
|
||||
in [#19027](https://github.com/google-gemini/gemini-cli/pull/19027)
|
||||
- fix(plan): persist the approval mode in UI even when agent is thinking by
|
||||
@Adib234 in [#18955](https://github.com/google-gemini/gemini-cli/pull/18955)
|
||||
- feat(sdk): Implement dynamic system instructions by @mbleigh in
|
||||
[#18863](https://github.com/google-gemini/gemini-cli/pull/18863)
|
||||
- Docs: Refresh docs to organize and standardize reference materials. by
|
||||
@jkcinouye in [#18403](https://github.com/google-gemini/gemini-cli/pull/18403)
|
||||
- fix windows escaping (and broken tests) by @scidomino in
|
||||
[#19011](https://github.com/google-gemini/gemini-cli/pull/19011)
|
||||
- refactor: use `CoreToolCallStatus` in the the history data model by @jerop in
|
||||
[#19033](https://github.com/google-gemini/gemini-cli/pull/19033)
|
||||
- feat(cleanup): enable 30-day session retention by default by @skeshive in
|
||||
[#18854](https://github.com/google-gemini/gemini-cli/pull/18854)
|
||||
- feat(plan): hide plan write and edit operations on plans in Plan Mode by
|
||||
@jerop in [#19012](https://github.com/google-gemini/gemini-cli/pull/19012)
|
||||
- bug(ui) fix flicker refreshing background color by @jacob314 in
|
||||
[#19041](https://github.com/google-gemini/gemini-cli/pull/19041)
|
||||
- chore: fix dep vulnerabilities by @scidomino in
|
||||
[#19036](https://github.com/google-gemini/gemini-cli/pull/19036)
|
||||
- Revamp automated changelog skill by @g-samroberts in
|
||||
[#18974](https://github.com/google-gemini/gemini-cli/pull/18974)
|
||||
- feat(sdk): implement support for custom skills by @mbleigh in
|
||||
[#19031](https://github.com/google-gemini/gemini-cli/pull/19031)
|
||||
- refactor(core): complete centralization of core tool definitions by
|
||||
[#19766](https://github.com/google-gemini/gemini-cli/pull/19766)
|
||||
- feat(policy): Support MCP Server Wildcards in Policy Engine by @jerop in
|
||||
[#20024](https://github.com/google-gemini/gemini-cli/pull/20024)
|
||||
- docs(CONTRIBUTING): update React DevTools version to 6 by @mmgok in
|
||||
[#20014](https://github.com/google-gemini/gemini-cli/pull/20014)
|
||||
- feat(core): optimize tool descriptions and schemas for Gemini 3 by
|
||||
@aishaneeshah in
|
||||
[#18991](https://github.com/google-gemini/gemini-cli/pull/18991)
|
||||
- feat: add /commands reload to refresh custom TOML commands by @korade-krushna
|
||||
in [#19078](https://github.com/google-gemini/gemini-cli/pull/19078)
|
||||
- fix(cli): wrap terminal capability queries in hidden sequence by @srithreepo
|
||||
in [#19080](https://github.com/google-gemini/gemini-cli/pull/19080)
|
||||
- fix(workflows): fix GitHub App token permissions for maintainer detection by
|
||||
@bdmorgan in [#19139](https://github.com/google-gemini/gemini-cli/pull/19139)
|
||||
- test: fix hook integration test flakiness on Windows CI by @NTaylorMullen in
|
||||
[#18665](https://github.com/google-gemini/gemini-cli/pull/18665)
|
||||
- fix(core): Encourage non-interactive flags for scaffolding commands by
|
||||
@NTaylorMullen in
|
||||
[#18804](https://github.com/google-gemini/gemini-cli/pull/18804)
|
||||
- fix(core): propagate User-Agent header to setup-phase CodeAssist API calls by
|
||||
@gsquared94 in
|
||||
[#19182](https://github.com/google-gemini/gemini-cli/pull/19182)
|
||||
- docs: document .agents/skills alias and discovery precedence by @kevmoo in
|
||||
[#19166](https://github.com/google-gemini/gemini-cli/pull/19166)
|
||||
- feat(cli): add loading state to new agents notification by @sehoon38 in
|
||||
[#19190](https://github.com/google-gemini/gemini-cli/pull/19190)
|
||||
- Add base branch to workflow. by @g-samroberts in
|
||||
[#19189](https://github.com/google-gemini/gemini-cli/pull/19189)
|
||||
- feat(cli): handle invalid model names in useQuotaAndFallback by @sehoon38 in
|
||||
[#19222](https://github.com/google-gemini/gemini-cli/pull/19222)
|
||||
- docs: custom themes in extensions by @jackwotherspoon in
|
||||
[#19219](https://github.com/google-gemini/gemini-cli/pull/19219)
|
||||
- Disable workspace settings when starting GCLI in the home directory. by
|
||||
@kevinjwang1 in
|
||||
[#19034](https://github.com/google-gemini/gemini-cli/pull/19034)
|
||||
- feat(cli): refactor model command to support set and manage subcommands by
|
||||
@sehoon38 in [#19221](https://github.com/google-gemini/gemini-cli/pull/19221)
|
||||
- Add refresh/reload aliases to slash command subcommands by @korade-krushna in
|
||||
[#19218](https://github.com/google-gemini/gemini-cli/pull/19218)
|
||||
- refactor: consolidate development rules and add cli guidelines by @jacob314 in
|
||||
[#19214](https://github.com/google-gemini/gemini-cli/pull/19214)
|
||||
- chore(ui): remove outdated tip about model routing by @sehoon38 in
|
||||
[#19226](https://github.com/google-gemini/gemini-cli/pull/19226)
|
||||
- feat(core): support custom reasoning models by default by @NTaylorMullen in
|
||||
[#19227](https://github.com/google-gemini/gemini-cli/pull/19227)
|
||||
- Add Solarized Dark and Solarized Light themes by @rmedranollamas in
|
||||
[#19064](https://github.com/google-gemini/gemini-cli/pull/19064)
|
||||
- fix(telemetry): replace JSON.stringify with safeJsonStringify in file
|
||||
exporters by @gsquared94 in
|
||||
[#19244](https://github.com/google-gemini/gemini-cli/pull/19244)
|
||||
- feat(telemetry): add keychain availability and token storage metrics by
|
||||
@abhipatel12 in
|
||||
[#18971](https://github.com/google-gemini/gemini-cli/pull/18971)
|
||||
- feat(cli): update approval mode cycle order by @jerop in
|
||||
[#19254](https://github.com/google-gemini/gemini-cli/pull/19254)
|
||||
- refactor(cli): code review cleanup fix for tab+tab by @jacob314 in
|
||||
[#18967](https://github.com/google-gemini/gemini-cli/pull/18967)
|
||||
- feat(plan): support project exploration without planning when in plan mode by
|
||||
@Adib234 in [#18992](https://github.com/google-gemini/gemini-cli/pull/18992)
|
||||
- feat: add role-specific statistics to telemetry and UI (cont. #15234) by
|
||||
@yunaseoul in [#18824](https://github.com/google-gemini/gemini-cli/pull/18824)
|
||||
- feat(cli): remove Plan Mode from rotation when actively working by @jerop in
|
||||
[#19262](https://github.com/google-gemini/gemini-cli/pull/19262)
|
||||
- Fix side breakage where anchors don't work in slugs. by @g-samroberts in
|
||||
[#19261](https://github.com/google-gemini/gemini-cli/pull/19261)
|
||||
- feat(config): add setting to make directory tree context configurable by
|
||||
@kevin-ramdass in
|
||||
[#19053](https://github.com/google-gemini/gemini-cli/pull/19053)
|
||||
- fix(acp): Wait for mcp initialization in acp (#18893) by @Mervap in
|
||||
[#18894](https://github.com/google-gemini/gemini-cli/pull/18894)
|
||||
- docs: format UTC times in releases doc by @pavan-sh in
|
||||
[#18169](https://github.com/google-gemini/gemini-cli/pull/18169)
|
||||
- Docs: Clarify extensions documentation. by @jkcinouye in
|
||||
[#19277](https://github.com/google-gemini/gemini-cli/pull/19277)
|
||||
- refactor(core): modularize tool definitions by model family by @aishaneeshah
|
||||
in [#19269](https://github.com/google-gemini/gemini-cli/pull/19269)
|
||||
- fix(paths): Add cross-platform path normalization by @spencer426 in
|
||||
[#18939](https://github.com/google-gemini/gemini-cli/pull/18939)
|
||||
- feat(core): experimental in-progress steering hints (1 of 3) by @joshualitt in
|
||||
[#19008](https://github.com/google-gemini/gemini-cli/pull/19008)
|
||||
[#19643](https://github.com/google-gemini/gemini-cli/pull/19643)
|
||||
- feat(core): implement experimental direct web fetch by @mbleigh in
|
||||
[#19557](https://github.com/google-gemini/gemini-cli/pull/19557)
|
||||
- feat(core): replace expected_replacements with allow_multiple in replace tool
|
||||
by @SandyTao520 in
|
||||
[#20033](https://github.com/google-gemini/gemini-cli/pull/20033)
|
||||
- fix(sandbox): harden image packaging integrity checks by @aviralgarg05 in
|
||||
[#19552](https://github.com/google-gemini/gemini-cli/pull/19552)
|
||||
- fix(core): allow environment variable expansion and explicit overrides for MCP
|
||||
servers by @galz10 in
|
||||
[#18837](https://github.com/google-gemini/gemini-cli/pull/18837)
|
||||
- feat(policy): Implement Tool Annotation Matching in Policy Engine by @jerop in
|
||||
[#20029](https://github.com/google-gemini/gemini-cli/pull/20029)
|
||||
- fix(core): prevent utility calls from changing session active model by
|
||||
@adamfweidman in
|
||||
[#20035](https://github.com/google-gemini/gemini-cli/pull/20035)
|
||||
- fix(cli): skip workspace policy loading when in home directory by
|
||||
@Abhijit-2592 in
|
||||
[#20054](https://github.com/google-gemini/gemini-cli/pull/20054)
|
||||
- fix(scripts): Add Windows (win32/x64) support to lint.js by @ZafeerMahmood in
|
||||
[#16193](https://github.com/google-gemini/gemini-cli/pull/16193)
|
||||
- fix(a2a-server): Remove unsafe type assertions in agent by @Nixxx19 in
|
||||
[#19723](https://github.com/google-gemini/gemini-cli/pull/19723)
|
||||
- Fix: Handle corrupted token file gracefully when switching auth types (#19845)
|
||||
by @Nixxx19 in
|
||||
[#19850](https://github.com/google-gemini/gemini-cli/pull/19850)
|
||||
- fix critical dep vulnerability by @scidomino in
|
||||
[#20087](https://github.com/google-gemini/gemini-cli/pull/20087)
|
||||
- Add new setting to configure maxRetries by @kevinjwang1 in
|
||||
[#20064](https://github.com/google-gemini/gemini-cli/pull/20064)
|
||||
- Stabilize tests. by @gundermanc in
|
||||
[#20095](https://github.com/google-gemini/gemini-cli/pull/20095)
|
||||
- make windows tests mandatory by @scidomino in
|
||||
[#20096](https://github.com/google-gemini/gemini-cli/pull/20096)
|
||||
- Add 3.1 pro preview to behavioral evals. by @gundermanc in
|
||||
[#20088](https://github.com/google-gemini/gemini-cli/pull/20088)
|
||||
- feat:PR-rate-limit by @JagjeevanAK in
|
||||
[#19804](https://github.com/google-gemini/gemini-cli/pull/19804)
|
||||
- feat(cli): allow expanding full details of MCP tool on approval by @y-okt in
|
||||
[#19916](https://github.com/google-gemini/gemini-cli/pull/19916)
|
||||
- feat(security): Introduce Conseca framework by @shrishabh in
|
||||
[#13193](https://github.com/google-gemini/gemini-cli/pull/13193)
|
||||
- fix(cli): Remove unsafe type assertions in activityLogger #19713 by @Nixxx19
|
||||
in [#19745](https://github.com/google-gemini/gemini-cli/pull/19745)
|
||||
- feat: implement AfterTool tail tool calls by @googlestrobe in
|
||||
[#18486](https://github.com/google-gemini/gemini-cli/pull/18486)
|
||||
- ci(actions): fix PR rate limiter excluding maintainers by @scidomino in
|
||||
[#20117](https://github.com/google-gemini/gemini-cli/pull/20117)
|
||||
- Shortcuts: Move SectionHeader title below top line and refine styling by
|
||||
@keithguerin in
|
||||
[#18721](https://github.com/google-gemini/gemini-cli/pull/18721)
|
||||
- refactor(ui): Update and simplify use of gray colors in themes by @keithguerin
|
||||
in [#20141](https://github.com/google-gemini/gemini-cli/pull/20141)
|
||||
- fix punycode2 by @jacob314 in
|
||||
[#20154](https://github.com/google-gemini/gemini-cli/pull/20154)
|
||||
- feat(ide): add GEMINI_CLI_IDE_PID env var to override IDE process detection by
|
||||
@kiryltech in [#15842](https://github.com/google-gemini/gemini-cli/pull/15842)
|
||||
- feat(policy): Propagate Tool Annotations for MCP Servers by @jerop in
|
||||
[#20083](https://github.com/google-gemini/gemini-cli/pull/20083)
|
||||
- fix(a2a-server): pass allowedTools settings to core Config by @reyyanxahmed in
|
||||
[#19680](https://github.com/google-gemini/gemini-cli/pull/19680)
|
||||
- feat(mcp): add progress bar, throttling, and input validation for MCP tool
|
||||
progress by @jasmeetsb in
|
||||
[#19772](https://github.com/google-gemini/gemini-cli/pull/19772)
|
||||
- feat(policy): centralize plan mode tool visibility in policy engine by @jerop
|
||||
in [#20178](https://github.com/google-gemini/gemini-cli/pull/20178)
|
||||
- feat(browser): implement experimental browser agent by @gsquared94 in
|
||||
[#19284](https://github.com/google-gemini/gemini-cli/pull/19284)
|
||||
- feat(plan): summarize work after executing a plan by @jerop in
|
||||
[#19432](https://github.com/google-gemini/gemini-cli/pull/19432)
|
||||
- fix(core): create new McpClient on restart to apply updated config by @h30s in
|
||||
[#20126](https://github.com/google-gemini/gemini-cli/pull/20126)
|
||||
- Changelog for v0.30.0-preview.5 by @gemini-cli-robot in
|
||||
[#20107](https://github.com/google-gemini/gemini-cli/pull/20107)
|
||||
- Update packages. by @jacob314 in
|
||||
[#20152](https://github.com/google-gemini/gemini-cli/pull/20152)
|
||||
- Fix extension env dir loading issue by @chrstnb in
|
||||
[#20198](https://github.com/google-gemini/gemini-cli/pull/20198)
|
||||
- restrict /assign to help-wanted issues by @scidomino in
|
||||
[#20207](https://github.com/google-gemini/gemini-cli/pull/20207)
|
||||
- feat(plan): inject message when user manually exits Plan mode by @jerop in
|
||||
[#20203](https://github.com/google-gemini/gemini-cli/pull/20203)
|
||||
- feat(extensions): enforce folder trust for local extension install by @galz10
|
||||
in [#19703](https://github.com/google-gemini/gemini-cli/pull/19703)
|
||||
- feat(hooks): adds support for RuntimeHook functions. by @mbleigh in
|
||||
[#19598](https://github.com/google-gemini/gemini-cli/pull/19598)
|
||||
- Docs: Update UI links. by @jkcinouye in
|
||||
[#20224](https://github.com/google-gemini/gemini-cli/pull/20224)
|
||||
- feat: prompt users to run /terminal-setup with yes/no by @ishaanxgupta in
|
||||
[#16235](https://github.com/google-gemini/gemini-cli/pull/16235)
|
||||
- fix: additional high vulnerabilities (minimatch, cross-spawn) by @adamfweidman
|
||||
in [#20221](https://github.com/google-gemini/gemini-cli/pull/20221)
|
||||
- feat(telemetry): Add context breakdown to API response event by @SandyTao520
|
||||
in [#19699](https://github.com/google-gemini/gemini-cli/pull/19699)
|
||||
- Docs: Add nested sub-folders for related topics by @g-samroberts in
|
||||
[#20235](https://github.com/google-gemini/gemini-cli/pull/20235)
|
||||
- feat(plan): support automatic model switching for Plan Mode by @jerop in
|
||||
[#20240](https://github.com/google-gemini/gemini-cli/pull/20240)
|
||||
|
||||
**Full changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.29.0-preview.5...v0.30.0-preview.5
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.30.0-preview.6...v0.31.0-preview.0
|
||||
|
||||
@@ -119,6 +119,7 @@ These are the only allowed tools:
|
||||
- **Planning (Write):** [`write_file`] and [`replace`] only allowed for `.md`
|
||||
files in the `~/.gemini/tmp/<project>/<session-id>/plans/` directory or your
|
||||
[custom plans directory](#custom-plan-directory-and-policies).
|
||||
- **Memory:** [`save_memory`]
|
||||
- **Skills:** [`activate_skill`] (allows loading specialized instructions and
|
||||
resources in a read-only manner)
|
||||
|
||||
@@ -277,6 +278,7 @@ performance. You can disable this automatic switching in your settings:
|
||||
[`google_web_search`]: /docs/tools/web-search.md
|
||||
[`replace`]: /docs/tools/file-system.md#6-replace-edit
|
||||
[MCP tools]: /docs/tools/mcp-server.md
|
||||
[`save_memory`]: /docs/tools/memory.md
|
||||
[`activate_skill`]: /docs/cli/skills.md
|
||||
[subagents]: /docs/core/subagents.md
|
||||
[policy engine]: /docs/reference/policy-engine.md
|
||||
|
||||
@@ -82,12 +82,13 @@ they appear in the UI.
|
||||
|
||||
### Model
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ----------------------- | ---------------------------- | -------------------------------------------------------------------------------------- | ------- |
|
||||
| Max Session Turns | `model.maxSessionTurns` | Maximum number of user/model/tool turns to keep in a session. -1 means unlimited. | `-1` |
|
||||
| Compression Threshold | `model.compressionThreshold` | The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3). | `0.5` |
|
||||
| Disable Loop Detection | `model.disableLoopDetection` | Disable automatic detection and prevention of infinite loops. | `false` |
|
||||
| Skip Next Speaker Check | `model.skipNextSpeakerCheck` | Skip the next speaker check. | `true` |
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ----------------------- | ---------------------------- | -------------------------------------------------------------------------------------- | ----------- |
|
||||
| Model | `model.name` | The Gemini model to use for conversations. | `undefined` |
|
||||
| Max Session Turns | `model.maxSessionTurns` | Maximum number of user/model/tool turns to keep in a session. -1 means unlimited. | `-1` |
|
||||
| Compression Threshold | `model.compressionThreshold` | The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3). | `0.5` |
|
||||
| Disable Loop Detection | `model.disableLoopDetection` | Disable automatic detection and prevention of infinite loops. | `false` |
|
||||
| Skip Next Speaker Check | `model.skipNextSpeakerCheck` | Skip the next speaker check. | `true` |
|
||||
|
||||
### Context
|
||||
|
||||
|
||||
@@ -19,16 +19,7 @@ can find your npm cache path by running `npm config get cache`.
|
||||
rm -rf "$(npm config get cache)/_npx"
|
||||
```
|
||||
|
||||
**For Windows**
|
||||
|
||||
_Command Prompt_
|
||||
|
||||
```cmd
|
||||
:: The path is typically %LocalAppData%\npm-cache\_npx
|
||||
rmdir /s /q "%LocalAppData%\npm-cache\_npx"
|
||||
```
|
||||
|
||||
_PowerShell_
|
||||
**For Windows (PowerShell)**
|
||||
|
||||
```powershell
|
||||
# The path is typically $env:LocalAppData\npm-cache\_npx
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { appEvalTest } from './app-test-helper.js';
|
||||
|
||||
describe('generalist_delegation', () => {
|
||||
// --- Positive Evals (Should Delegate) ---
|
||||
|
||||
appEvalTest('USUALLY_PASSES', {
|
||||
name: 'should delegate batch error fixing to generalist agent',
|
||||
configOverrides: {
|
||||
agents: {
|
||||
overrides: {
|
||||
generalist: { enabled: true },
|
||||
},
|
||||
},
|
||||
experimental: {
|
||||
enableAgents: true,
|
||||
},
|
||||
excludeTools: ['run_shell_command'],
|
||||
},
|
||||
files: {
|
||||
'file1.ts': 'console.log("no semi")',
|
||||
'file2.ts': 'console.log("no semi")',
|
||||
'file3.ts': 'console.log("no semi")',
|
||||
'file4.ts': 'console.log("no semi")',
|
||||
'file5.ts': 'console.log("no semi")',
|
||||
'file6.ts': 'console.log("no semi")',
|
||||
'file7.ts': 'console.log("no semi")',
|
||||
'file8.ts': 'console.log("no semi")',
|
||||
'file9.ts': 'console.log("no semi")',
|
||||
'file10.ts': 'console.log("no semi")',
|
||||
},
|
||||
prompt:
|
||||
'I have 10 files (file1.ts to file10.ts) that are missing semicolons. Can you fix them?',
|
||||
setup: async (rig) => {
|
||||
rig.setBreakpoint(['generalist']);
|
||||
},
|
||||
assert: async (rig) => {
|
||||
const confirmation = await rig.waitForPendingConfirmation(
|
||||
'generalist',
|
||||
60000,
|
||||
);
|
||||
expect(
|
||||
confirmation,
|
||||
'Expected a tool call for generalist agent',
|
||||
).toBeTruthy();
|
||||
await rig.resolveTool(confirmation);
|
||||
await rig.waitForIdle(60000);
|
||||
},
|
||||
});
|
||||
|
||||
appEvalTest('USUALLY_PASSES', {
|
||||
name: 'should autonomously delegate complex batch task to generalist agent',
|
||||
configOverrides: {
|
||||
agents: {
|
||||
overrides: {
|
||||
generalist: { enabled: true },
|
||||
},
|
||||
},
|
||||
experimental: {
|
||||
enableAgents: true,
|
||||
},
|
||||
excludeTools: ['run_shell_command'],
|
||||
},
|
||||
files: {
|
||||
'src/a.ts': 'export const a = 1;',
|
||||
'src/b.ts': 'export const b = 2;',
|
||||
'src/c.ts': 'export const c = 3;',
|
||||
'src/d.ts': 'export const d = 4;',
|
||||
'src/e.ts': 'export const e = 5;',
|
||||
},
|
||||
prompt:
|
||||
'Please update all files in the src directory. For each file, add a comment at the top that says "Processed by Gemini".',
|
||||
setup: async (rig) => {
|
||||
rig.setBreakpoint(['generalist']);
|
||||
},
|
||||
assert: async (rig) => {
|
||||
const confirmation = await rig.waitForPendingConfirmation(
|
||||
'generalist',
|
||||
60000,
|
||||
);
|
||||
expect(
|
||||
confirmation,
|
||||
'Expected autonomously delegate to generalist for batch task',
|
||||
).toBeTruthy();
|
||||
await rig.resolveTool(confirmation);
|
||||
await rig.waitForIdle(60000);
|
||||
},
|
||||
});
|
||||
|
||||
// --- Negative Evals (Should NOT Delegate - Assertive Handling) ---
|
||||
|
||||
appEvalTest('USUALLY_PASSES', {
|
||||
name: 'should NOT delegate simple read and fix to generalist agent',
|
||||
configOverrides: {
|
||||
agents: {
|
||||
overrides: {
|
||||
generalist: { enabled: true },
|
||||
},
|
||||
},
|
||||
experimental: {
|
||||
enableAgents: true,
|
||||
},
|
||||
excludeTools: ['run_shell_command'],
|
||||
},
|
||||
files: {
|
||||
'README.md': 'This is a proyect.',
|
||||
},
|
||||
prompt:
|
||||
'There is a typo in README.md ("proyect"). Please fix it to "project".',
|
||||
setup: async (rig) => {
|
||||
// Break on everything to see what it calls
|
||||
rig.setBreakpoint(['*']);
|
||||
},
|
||||
assert: async (rig) => {
|
||||
await rig.drainBreakpointsUntilIdle((confirmation) => {
|
||||
expect(
|
||||
confirmation.toolName,
|
||||
`Agent should NOT have delegated to generalist.`,
|
||||
).not.toBe('generalist');
|
||||
});
|
||||
|
||||
const output = rig.getStaticOutput();
|
||||
expect(output).toMatch(/project/i);
|
||||
},
|
||||
});
|
||||
|
||||
appEvalTest('USUALLY_PASSES', {
|
||||
name: 'should NOT delegate simple direct question to generalist agent',
|
||||
configOverrides: {
|
||||
agents: {
|
||||
overrides: {
|
||||
generalist: { enabled: true },
|
||||
},
|
||||
},
|
||||
experimental: {
|
||||
enableAgents: true,
|
||||
},
|
||||
excludeTools: ['run_shell_command'],
|
||||
},
|
||||
files: {
|
||||
'src/VERSION': '1.2.3',
|
||||
},
|
||||
prompt: 'Can you tell me the version number in the src folder?',
|
||||
setup: async (rig) => {
|
||||
rig.setBreakpoint(['*']);
|
||||
},
|
||||
assert: async (rig) => {
|
||||
await rig.drainBreakpointsUntilIdle((confirmation) => {
|
||||
expect(
|
||||
confirmation.toolName,
|
||||
`Agent should NOT have delegated to generalist.`,
|
||||
).not.toBe('generalist');
|
||||
});
|
||||
|
||||
const output = rig.getStaticOutput();
|
||||
expect(output).toMatch(/1\.2\.3/);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -93,7 +93,7 @@ describe('grep_search_functionality', () => {
|
||||
});
|
||||
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should search only within the specified include glob',
|
||||
name: 'should search only within the specified include_pattern glob',
|
||||
files: {
|
||||
'file.js': 'my_function();',
|
||||
'file.ts': 'my_function();',
|
||||
@@ -105,19 +105,19 @@ describe('grep_search_functionality', () => {
|
||||
undefined,
|
||||
(args) => {
|
||||
const params = JSON.parse(args);
|
||||
return params.include === '*.js';
|
||||
return params.include_pattern === '*.js';
|
||||
},
|
||||
);
|
||||
expect(
|
||||
wasToolCalled,
|
||||
'Expected grep_search to be called with include: "*.js"',
|
||||
'Expected grep_search to be called with include_pattern: "*.js"',
|
||||
).toBe(true);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: [/file.js/],
|
||||
forbiddenContent: [/file.ts/],
|
||||
testName: `${TEST_PREFIX}include glob search`,
|
||||
testName: `${TEST_PREFIX}include_pattern glob search`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -55,8 +55,8 @@ describe('file-system', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to write a file', async () => {
|
||||
await rig.setup('should be able to write a file', {
|
||||
it('should be able to write a hello world message to a file', async () => {
|
||||
await rig.setup('should be able to write a hello world message to a file', {
|
||||
settings: { tools: { core: ['write_file', 'replace', 'read_file'] } },
|
||||
});
|
||||
rig.createFile('test.txt', '');
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('Plan Mode', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow write_file only in the plans directory in plan mode', async () => {
|
||||
it.skip('should allow write_file only in the plans directory in plan mode', async () => {
|
||||
await rig.setup(
|
||||
'should allow write_file only in the plans directory in plan mode',
|
||||
{
|
||||
|
||||
@@ -102,7 +102,10 @@ describe('ripgrep-real-direct', () => {
|
||||
'console.log("hello");\n',
|
||||
);
|
||||
|
||||
const invocation = tool.build({ pattern: 'hello', include: '*.js' });
|
||||
const invocation = tool.build({
|
||||
pattern: 'hello',
|
||||
include_pattern: '*.js',
|
||||
});
|
||||
const result = await invocation.execute(new AbortController().signal);
|
||||
|
||||
expect(result.llmContent).toContain('Found 1 match');
|
||||
|
||||
@@ -22,8 +22,8 @@ describe('write_file', () => {
|
||||
|
||||
afterEach(async () => await rig.cleanup());
|
||||
|
||||
it('should be able to write a file', async () => {
|
||||
await rig.setup('should be able to write a file', {
|
||||
it('should be able to write a joke to a file', async () => {
|
||||
await rig.setup('should be able to write a joke to a file', {
|
||||
settings: { tools: { core: ['write_file', 'read_file'] } },
|
||||
});
|
||||
const prompt = `show me an example of using the write tool. put a dad joke in dad.txt`;
|
||||
|
||||
@@ -13,34 +13,21 @@ import {
|
||||
afterEach,
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { coreEvents } from '@google/gemini-cli-core';
|
||||
import { type Argv } from 'yargs';
|
||||
import { handleLink, linkCommand } from './link.js';
|
||||
import { ExtensionManager } from '../../config/extension-manager.js';
|
||||
import { loadSettings, type LoadedSettings } from '../../config/settings.js';
|
||||
import { getErrorMessage } from '../../utils/errors.js';
|
||||
|
||||
// Mock dependencies
|
||||
const emitConsoleLog = vi.hoisted(() => vi.fn());
|
||||
const debugLogger = vi.hoisted(() => ({
|
||||
log: vi.fn((message, ...args) => {
|
||||
emitConsoleLog('log', format(message, ...args));
|
||||
}),
|
||||
error: vi.fn((message, ...args) => {
|
||||
emitConsoleLog('error', format(message, ...args));
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import('@google/gemini-cli-core')>();
|
||||
return {
|
||||
...actual,
|
||||
coreEvents: {
|
||||
emitConsoleLog,
|
||||
},
|
||||
debugLogger,
|
||||
};
|
||||
const { mockCoreDebugLogger } = await import(
|
||||
'../../test-utils/mockDebugLogger.js'
|
||||
);
|
||||
return mockCoreDebugLogger(
|
||||
await importOriginal<typeof import('@google/gemini-cli-core')>(),
|
||||
{ stripAnsi: true },
|
||||
);
|
||||
});
|
||||
|
||||
vi.mock('../../config/extension-manager.js');
|
||||
@@ -95,7 +82,7 @@ describe('extensions link command', () => {
|
||||
source: '/local/path/to/extension',
|
||||
type: 'link',
|
||||
});
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
'Extension "my-linked-extension" linked successfully and enabled.',
|
||||
);
|
||||
@@ -116,7 +103,7 @@ describe('extensions link command', () => {
|
||||
|
||||
await handleLink({ path: '/local/path/to/extension' });
|
||||
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Link failed message',
|
||||
);
|
||||
|
||||
@@ -5,33 +5,22 @@
|
||||
*/
|
||||
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { coreEvents } from '@google/gemini-cli-core';
|
||||
import { handleList, listCommand } from './list.js';
|
||||
import { ExtensionManager } from '../../config/extension-manager.js';
|
||||
import { loadSettings, type LoadedSettings } from '../../config/settings.js';
|
||||
import { getErrorMessage } from '../../utils/errors.js';
|
||||
|
||||
// Mock dependencies
|
||||
const emitConsoleLog = vi.hoisted(() => vi.fn());
|
||||
const debugLogger = vi.hoisted(() => ({
|
||||
log: vi.fn((message, ...args) => {
|
||||
emitConsoleLog('log', format(message, ...args));
|
||||
}),
|
||||
error: vi.fn((message, ...args) => {
|
||||
emitConsoleLog('error', format(message, ...args));
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import('@google/gemini-cli-core')>();
|
||||
return {
|
||||
...actual,
|
||||
coreEvents: {
|
||||
emitConsoleLog,
|
||||
const { mockCoreDebugLogger } = await import(
|
||||
'../../test-utils/mockDebugLogger.js'
|
||||
);
|
||||
return mockCoreDebugLogger(
|
||||
await importOriginal<typeof import('@google/gemini-cli-core')>(),
|
||||
{
|
||||
stripAnsi: false,
|
||||
},
|
||||
debugLogger,
|
||||
};
|
||||
);
|
||||
});
|
||||
|
||||
vi.mock('../../config/extension-manager.js');
|
||||
@@ -71,7 +60,7 @@ describe('extensions list command', () => {
|
||||
.mockResolvedValue([]);
|
||||
await handleList();
|
||||
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
'No extensions installed.',
|
||||
);
|
||||
@@ -85,7 +74,7 @@ describe('extensions list command', () => {
|
||||
.mockResolvedValue([]);
|
||||
await handleList({ outputFormat: 'json' });
|
||||
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith('log', '[]');
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith('log', '[]');
|
||||
mockCwd.mockRestore();
|
||||
});
|
||||
|
||||
@@ -103,7 +92,7 @@ describe('extensions list command', () => {
|
||||
);
|
||||
await handleList();
|
||||
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
'ext1@1.0.0\n\next2@2.0.0',
|
||||
);
|
||||
@@ -121,7 +110,7 @@ describe('extensions list command', () => {
|
||||
.mockResolvedValue(extensions);
|
||||
await handleList({ outputFormat: 'json' });
|
||||
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
JSON.stringify(extensions, null, 2),
|
||||
);
|
||||
@@ -142,7 +131,7 @@ describe('extensions list command', () => {
|
||||
|
||||
await handleList();
|
||||
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'List failed message',
|
||||
);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { handleDisable, disableCommand } from './disable.js';
|
||||
import {
|
||||
loadSettings,
|
||||
@@ -14,12 +13,12 @@ import {
|
||||
type LoadableSettingScope,
|
||||
} from '../../config/settings.js';
|
||||
|
||||
const emitConsoleLog = vi.hoisted(() => vi.fn());
|
||||
const debugLogger = vi.hoisted(() => ({
|
||||
log: vi.fn((message, ...args) => {
|
||||
emitConsoleLog('log', format(message, ...args));
|
||||
}),
|
||||
}));
|
||||
const { emitConsoleLog, debugLogger } = await vi.hoisted(async () => {
|
||||
const { createMockDebugLogger } = await import(
|
||||
'../../test-utils/mockDebugLogger.js'
|
||||
);
|
||||
return createMockDebugLogger({ stripAnsi: true });
|
||||
});
|
||||
|
||||
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
const actual =
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { handleEnable, enableCommand } from './enable.js';
|
||||
import {
|
||||
loadSettings,
|
||||
@@ -13,12 +12,12 @@ import {
|
||||
type LoadedSettings,
|
||||
} from '../../config/settings.js';
|
||||
|
||||
const emitConsoleLog = vi.hoisted(() => vi.fn());
|
||||
const debugLogger = vi.hoisted(() => ({
|
||||
log: vi.fn((message, ...args) => {
|
||||
emitConsoleLog('log', format(message, ...args));
|
||||
}),
|
||||
}));
|
||||
const { emitConsoleLog, debugLogger } = await vi.hoisted(async () => {
|
||||
const { createMockDebugLogger } = await import(
|
||||
'../../test-utils/mockDebugLogger.js'
|
||||
);
|
||||
return createMockDebugLogger({ stripAnsi: true });
|
||||
});
|
||||
|
||||
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
const actual =
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
|
||||
const mockInstallSkill = vi.hoisted(() => vi.fn());
|
||||
const mockRequestConsentNonInteractive = vi.hoisted(() => vi.fn());
|
||||
@@ -19,11 +19,17 @@ vi.mock('../../config/extensions/consent.js', () => ({
|
||||
skillsConsentString: mockSkillsConsentString,
|
||||
}));
|
||||
|
||||
const { debugLogger, emitConsoleLog } = await vi.hoisted(async () => {
|
||||
const { createMockDebugLogger } = await import(
|
||||
'../../test-utils/mockDebugLogger.js'
|
||||
);
|
||||
return createMockDebugLogger({ stripAnsi: true });
|
||||
});
|
||||
|
||||
vi.mock('@google/gemini-cli-core', () => ({
|
||||
debugLogger: { log: vi.fn(), error: vi.fn() },
|
||||
debugLogger,
|
||||
}));
|
||||
|
||||
import { debugLogger } from '@google/gemini-cli-core';
|
||||
import { handleInstall, installCommand } from './install.js';
|
||||
|
||||
describe('skill install command', () => {
|
||||
@@ -63,10 +69,12 @@ describe('skill install command', () => {
|
||||
expect.any(Function),
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(debugLogger.log).toHaveBeenCalledWith(
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('Successfully installed skill: test-skill'),
|
||||
);
|
||||
expect(debugLogger.log).toHaveBeenCalledWith(
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('location: /mock/user/skills/test-skill'),
|
||||
);
|
||||
expect(mockRequestConsentNonInteractive).toHaveBeenCalledWith(
|
||||
@@ -86,10 +94,11 @@ describe('skill install command', () => {
|
||||
});
|
||||
|
||||
expect(mockRequestConsentNonInteractive).not.toHaveBeenCalled();
|
||||
expect(debugLogger.log).toHaveBeenCalledWith(
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
'You have consented to the following:',
|
||||
);
|
||||
expect(debugLogger.log).toHaveBeenCalledWith('Mock Consent String');
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith('log', 'Mock Consent String');
|
||||
expect(mockInstallSkill).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -106,7 +115,8 @@ describe('skill install command', () => {
|
||||
source: 'https://example.com/repo.git',
|
||||
});
|
||||
|
||||
expect(debugLogger.error).toHaveBeenCalledWith(
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Skill installation cancelled by user.',
|
||||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(1);
|
||||
@@ -137,7 +147,7 @@ describe('skill install command', () => {
|
||||
|
||||
await handleInstall({ source: '/local/path' });
|
||||
|
||||
expect(debugLogger.error).toHaveBeenCalledWith('Install failed');
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith('error', 'Install failed');
|
||||
expect(process.exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,8 +15,15 @@ vi.mock('../../utils/skillUtils.js', () => ({
|
||||
linkSkill: mockLinkSkill,
|
||||
}));
|
||||
|
||||
const { debugLogger } = await vi.hoisted(async () => {
|
||||
const { createMockDebugLogger } = await import(
|
||||
'../../test-utils/mockDebugLogger.js'
|
||||
);
|
||||
return createMockDebugLogger({ stripAnsi: false });
|
||||
});
|
||||
|
||||
vi.mock('@google/gemini-cli-core', () => ({
|
||||
debugLogger: { log: vi.fn(), error: vi.fn() },
|
||||
debugLogger,
|
||||
}));
|
||||
|
||||
vi.mock('../../config/extensions/consent.js', () => ({
|
||||
@@ -24,8 +31,6 @@ vi.mock('../../config/extensions/consent.js', () => ({
|
||||
skillsConsentString: mockSkillsConsentString,
|
||||
}));
|
||||
|
||||
import { debugLogger } from '@google/gemini-cli-core';
|
||||
|
||||
describe('skills link command', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
@@ -5,33 +5,23 @@
|
||||
*/
|
||||
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { coreEvents } from '@google/gemini-cli-core';
|
||||
import { handleList, listCommand } from './list.js';
|
||||
import { loadSettings, type LoadedSettings } from '../../config/settings.js';
|
||||
import { loadCliConfig } from '../../config/config.js';
|
||||
import type { Config } from '@google/gemini-cli-core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const emitConsoleLog = vi.hoisted(() => vi.fn());
|
||||
const debugLogger = vi.hoisted(() => ({
|
||||
log: vi.fn((message, ...args) => {
|
||||
emitConsoleLog('log', format(message, ...args));
|
||||
}),
|
||||
error: vi.fn((message, ...args) => {
|
||||
emitConsoleLog('error', format(message, ...args));
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import('@google/gemini-cli-core')>();
|
||||
return {
|
||||
...actual,
|
||||
coreEvents: {
|
||||
emitConsoleLog,
|
||||
const { mockCoreDebugLogger } = await import(
|
||||
'../../test-utils/mockDebugLogger.js'
|
||||
);
|
||||
return mockCoreDebugLogger(
|
||||
await importOriginal<typeof import('@google/gemini-cli-core')>(),
|
||||
{
|
||||
stripAnsi: false,
|
||||
},
|
||||
debugLogger,
|
||||
};
|
||||
);
|
||||
});
|
||||
|
||||
vi.mock('../../config/settings.js');
|
||||
@@ -67,7 +57,7 @@ describe('skills list command', () => {
|
||||
|
||||
await handleList({});
|
||||
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
'No skills discovered.',
|
||||
);
|
||||
@@ -98,23 +88,23 @@ describe('skills list command', () => {
|
||||
|
||||
await handleList({});
|
||||
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
chalk.bold('Discovered Agent Skills:'),
|
||||
);
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('skill1'),
|
||||
);
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining(chalk.green('[Enabled]')),
|
||||
);
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('skill2'),
|
||||
);
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining(chalk.red('[Disabled]')),
|
||||
);
|
||||
@@ -146,11 +136,11 @@ describe('skills list command', () => {
|
||||
|
||||
// Default
|
||||
await handleList({ all: false });
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('regular'),
|
||||
);
|
||||
expect(emitConsoleLog).not.toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).not.toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('builtin'),
|
||||
);
|
||||
@@ -159,15 +149,15 @@ describe('skills list command', () => {
|
||||
|
||||
// With all: true
|
||||
await handleList({ all: true });
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('regular'),
|
||||
);
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('builtin'),
|
||||
);
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
expect(coreEvents.emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining(chalk.gray(' [Built-in]')),
|
||||
);
|
||||
|
||||
@@ -12,11 +12,17 @@ vi.mock('../../utils/skillUtils.js', () => ({
|
||||
uninstallSkill: mockUninstallSkill,
|
||||
}));
|
||||
|
||||
const { debugLogger, emitConsoleLog } = await vi.hoisted(async () => {
|
||||
const { createMockDebugLogger } = await import(
|
||||
'../../test-utils/mockDebugLogger.js'
|
||||
);
|
||||
return createMockDebugLogger({ stripAnsi: true });
|
||||
});
|
||||
|
||||
vi.mock('@google/gemini-cli-core', () => ({
|
||||
debugLogger: { log: vi.fn(), error: vi.fn() },
|
||||
debugLogger,
|
||||
}));
|
||||
|
||||
import { debugLogger } from '@google/gemini-cli-core';
|
||||
import { handleUninstall, uninstallCommand } from './uninstall.js';
|
||||
|
||||
describe('skill uninstall command', () => {
|
||||
@@ -45,10 +51,12 @@ describe('skill uninstall command', () => {
|
||||
});
|
||||
|
||||
expect(mockUninstallSkill).toHaveBeenCalledWith('test-skill', 'user');
|
||||
expect(debugLogger.log).toHaveBeenCalledWith(
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('Successfully uninstalled skill: test-skill'),
|
||||
);
|
||||
expect(debugLogger.log).toHaveBeenCalledWith(
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
expect.stringContaining('location: /mock/user/skills/test-skill'),
|
||||
);
|
||||
});
|
||||
@@ -71,7 +79,8 @@ describe('skill uninstall command', () => {
|
||||
|
||||
await handleUninstall({ name: 'test-skill' });
|
||||
|
||||
expect(debugLogger.error).toHaveBeenCalledWith(
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Skill "test-skill" is not installed in the user scope.',
|
||||
);
|
||||
});
|
||||
@@ -81,7 +90,7 @@ describe('skill uninstall command', () => {
|
||||
|
||||
await handleUninstall({ name: 'test-skill' });
|
||||
|
||||
expect(debugLogger.error).toHaveBeenCalledWith('Uninstall failed');
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith('error', 'Uninstall failed');
|
||||
expect(process.exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -859,6 +859,7 @@ export async function loadCliConfig(
|
||||
fakeResponses: argv.fakeResponses,
|
||||
recordResponses: argv.recordResponses,
|
||||
retryFetchErrors: settings.general?.retryFetchErrors,
|
||||
maxAttempts: settings.general?.maxAttempts,
|
||||
ptyInfo: ptyInfo?.name,
|
||||
disableLLMCorrection: settings.tools?.disableLLMCorrection,
|
||||
rawOutput: argv.rawOutput,
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { ExtensionManager } from './extension-manager.js';
|
||||
import { createTestMergedSettings } from './settings.js';
|
||||
import { createExtension } from '../test-utils/createExtension.js';
|
||||
import { EXTENSIONS_DIRECTORY_NAME } from './extensions/variables.js';
|
||||
|
||||
const mockHomedir = vi.hoisted(() => vi.fn(() => '/tmp/mock-home'));
|
||||
|
||||
vi.mock('os', async (importOriginal) => {
|
||||
const mockedOs = await importOriginal<typeof os>();
|
||||
return {
|
||||
...mockedOs,
|
||||
homedir: mockHomedir,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import('@google/gemini-cli-core')>();
|
||||
return {
|
||||
...actual,
|
||||
homedir: mockHomedir,
|
||||
};
|
||||
});
|
||||
|
||||
describe('ExtensionManager', () => {
|
||||
let tempHomeDir: string;
|
||||
let tempWorkspaceDir: string;
|
||||
let userExtensionsDir: string;
|
||||
let extensionManager: ExtensionManager;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
tempHomeDir = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), 'gemini-cli-test-home-'),
|
||||
);
|
||||
tempWorkspaceDir = fs.mkdtempSync(
|
||||
path.join(tempHomeDir, 'gemini-cli-test-workspace-'),
|
||||
);
|
||||
mockHomedir.mockReturnValue(tempHomeDir);
|
||||
userExtensionsDir = path.join(tempHomeDir, EXTENSIONS_DIRECTORY_NAME);
|
||||
fs.mkdirSync(userExtensionsDir, { recursive: true });
|
||||
|
||||
extensionManager = new ExtensionManager({
|
||||
settings: createTestMergedSettings(),
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
requestConsent: vi.fn().mockResolvedValue(true),
|
||||
requestSetting: null,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
try {
|
||||
fs.rmSync(tempHomeDir, { recursive: true, force: true });
|
||||
} catch (_e) {
|
||||
// Ignore
|
||||
}
|
||||
});
|
||||
|
||||
describe('loadExtensions parallel loading', () => {
|
||||
it('should prevent concurrent loading and return the same promise', async () => {
|
||||
createExtension({
|
||||
extensionsDir: userExtensionsDir,
|
||||
name: 'ext1',
|
||||
version: '1.0.0',
|
||||
});
|
||||
createExtension({
|
||||
extensionsDir: userExtensionsDir,
|
||||
name: 'ext2',
|
||||
version: '1.0.0',
|
||||
});
|
||||
|
||||
// Call loadExtensions twice concurrently
|
||||
const promise1 = extensionManager.loadExtensions();
|
||||
const promise2 = extensionManager.loadExtensions();
|
||||
|
||||
// They should resolve to the exact same array
|
||||
const [extensions1, extensions2] = await Promise.all([
|
||||
promise1,
|
||||
promise2,
|
||||
]);
|
||||
|
||||
expect(extensions1).toBe(extensions2);
|
||||
expect(extensions1).toHaveLength(2);
|
||||
|
||||
const names = extensions1.map((ext) => ext.name).sort();
|
||||
expect(names).toEqual(['ext1', 'ext2']);
|
||||
});
|
||||
|
||||
it('should throw an error if loadExtensions is called after it has already resolved', async () => {
|
||||
createExtension({
|
||||
extensionsDir: userExtensionsDir,
|
||||
name: 'ext1',
|
||||
version: '1.0.0',
|
||||
});
|
||||
|
||||
await extensionManager.loadExtensions();
|
||||
|
||||
await expect(extensionManager.loadExtensions()).rejects.toThrow(
|
||||
'Extensions already loaded, only load extensions once.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not throw if extension directory does not exist', async () => {
|
||||
fs.rmSync(userExtensionsDir, { recursive: true, force: true });
|
||||
|
||||
const extensions = await extensionManager.loadExtensions();
|
||||
expect(extensions).toEqual([]);
|
||||
});
|
||||
|
||||
it('should throw if there are duplicate extension names', async () => {
|
||||
// We manually create two extensions with different dirs but same name in config
|
||||
const ext1Dir = path.join(userExtensionsDir, 'ext1-dir');
|
||||
const ext2Dir = path.join(userExtensionsDir, 'ext2-dir');
|
||||
fs.mkdirSync(ext1Dir, { recursive: true });
|
||||
fs.mkdirSync(ext2Dir, { recursive: true });
|
||||
|
||||
const config = JSON.stringify({
|
||||
name: 'duplicate-ext',
|
||||
version: '1.0.0',
|
||||
});
|
||||
fs.writeFileSync(path.join(ext1Dir, 'gemini-extension.json'), config);
|
||||
fs.writeFileSync(
|
||||
path.join(ext1Dir, 'metadata.json'),
|
||||
JSON.stringify({ type: 'local', source: ext1Dir }),
|
||||
);
|
||||
|
||||
fs.writeFileSync(path.join(ext2Dir, 'gemini-extension.json'), config);
|
||||
fs.writeFileSync(
|
||||
path.join(ext2Dir, 'metadata.json'),
|
||||
JSON.stringify({ type: 'local', source: ext2Dir }),
|
||||
);
|
||||
|
||||
await expect(extensionManager.loadExtensions()).rejects.toThrow(
|
||||
'Extension with name duplicate-ext already was loaded.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should wait for loadExtensions to finish when loadExtension is called concurrently', async () => {
|
||||
// Create an initial extension that loadExtensions will find
|
||||
createExtension({
|
||||
extensionsDir: userExtensionsDir,
|
||||
name: 'ext1',
|
||||
version: '1.0.0',
|
||||
});
|
||||
|
||||
// Start the parallel load (it will read ext1)
|
||||
const loadAllPromise = extensionManager.loadExtensions();
|
||||
|
||||
// Create a second extension dynamically in a DIFFERENT directory
|
||||
// so that loadExtensions (which scans userExtensionsDir) doesn't find it.
|
||||
const externalDir = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), 'external-ext-'),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(externalDir, 'gemini-extension.json'),
|
||||
JSON.stringify({ name: 'ext2', version: '1.0.0' }),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(externalDir, 'metadata.json'),
|
||||
JSON.stringify({ type: 'local', source: externalDir }),
|
||||
);
|
||||
|
||||
// Concurrently call loadExtension (simulating an install or update)
|
||||
const loadSinglePromise = extensionManager.loadExtension(externalDir);
|
||||
|
||||
// Wait for both to complete
|
||||
await Promise.all([loadAllPromise, loadSinglePromise]);
|
||||
|
||||
// Both extensions should now be present in the loadedExtensions array
|
||||
const extensions = extensionManager.getExtensions();
|
||||
expect(extensions).toHaveLength(2);
|
||||
const names = extensions.map((ext) => ext.name).sort();
|
||||
expect(names).toEqual(['ext1', 'ext2']);
|
||||
|
||||
fs.rmSync(externalDir, { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -102,6 +102,7 @@ export class ExtensionManager extends ExtensionLoader {
|
||||
private telemetryConfig: Config;
|
||||
private workspaceDir: string;
|
||||
private loadedExtensions: GeminiCLIExtension[] | undefined;
|
||||
private loadingPromise: Promise<GeminiCLIExtension[]> | null = null;
|
||||
|
||||
constructor(options: ExtensionManagerParams) {
|
||||
super(options.eventEmitter);
|
||||
@@ -519,31 +520,103 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
throw new Error('Extensions already loaded, only load extensions once.');
|
||||
}
|
||||
|
||||
if (this.settings.admin.extensions.enabled === false) {
|
||||
this.loadedExtensions = [];
|
||||
return this.loadedExtensions;
|
||||
if (this.loadingPromise) {
|
||||
return this.loadingPromise;
|
||||
}
|
||||
|
||||
const extensionsDir = ExtensionStorage.getUserExtensionsDir();
|
||||
this.loadedExtensions = [];
|
||||
if (!fs.existsSync(extensionsDir)) {
|
||||
return this.loadedExtensions;
|
||||
}
|
||||
for (const subdir of fs.readdirSync(extensionsDir)) {
|
||||
const extensionDir = path.join(extensionsDir, subdir);
|
||||
await this.loadExtension(extensionDir);
|
||||
}
|
||||
return this.loadedExtensions;
|
||||
this.loadingPromise = (async () => {
|
||||
try {
|
||||
if (this.settings.admin.extensions.enabled === false) {
|
||||
this.loadedExtensions = [];
|
||||
return this.loadedExtensions;
|
||||
}
|
||||
|
||||
const extensionsDir = ExtensionStorage.getUserExtensionsDir();
|
||||
if (!fs.existsSync(extensionsDir)) {
|
||||
this.loadedExtensions = [];
|
||||
return this.loadedExtensions;
|
||||
}
|
||||
|
||||
const subdirs = await fs.promises.readdir(extensionsDir);
|
||||
const extensionPromises = subdirs.map((subdir) => {
|
||||
const extensionDir = path.join(extensionsDir, subdir);
|
||||
return this._buildExtension(extensionDir);
|
||||
});
|
||||
|
||||
const builtExtensionsOrNull = await Promise.all(extensionPromises);
|
||||
const builtExtensions = builtExtensionsOrNull.filter(
|
||||
(ext): ext is GeminiCLIExtension => ext !== null,
|
||||
);
|
||||
|
||||
const seenNames = new Set<string>();
|
||||
for (const ext of builtExtensions) {
|
||||
if (seenNames.has(ext.name)) {
|
||||
throw new Error(
|
||||
`Extension with name ${ext.name} already was loaded.`,
|
||||
);
|
||||
}
|
||||
seenNames.add(ext.name);
|
||||
}
|
||||
|
||||
this.loadedExtensions = builtExtensions;
|
||||
|
||||
await Promise.all(
|
||||
this.loadedExtensions.map((ext) => this.maybeStartExtension(ext)),
|
||||
);
|
||||
|
||||
return this.loadedExtensions;
|
||||
} finally {
|
||||
this.loadingPromise = null;
|
||||
}
|
||||
})();
|
||||
|
||||
return this.loadingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds `extension` to the list of extensions and starts it if appropriate.
|
||||
*
|
||||
* @internal visible for testing only
|
||||
*/
|
||||
private async loadExtension(
|
||||
async loadExtension(
|
||||
extensionDir: string,
|
||||
): Promise<GeminiCLIExtension | null> {
|
||||
if (this.loadingPromise) {
|
||||
await this.loadingPromise;
|
||||
}
|
||||
this.loadedExtensions ??= [];
|
||||
if (!fs.statSync(extensionDir).isDirectory()) {
|
||||
const extension = await this._buildExtension(extensionDir);
|
||||
if (!extension) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
this.getExtensions().find(
|
||||
(installed) => installed.name === extension.name,
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`Extension with name ${extension.name} already was loaded.`,
|
||||
);
|
||||
}
|
||||
|
||||
this.loadedExtensions = [...this.loadedExtensions, extension];
|
||||
await this.maybeStartExtension(extension);
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an extension without side effects (does not mutate loadedExtensions or start it).
|
||||
*/
|
||||
private async _buildExtension(
|
||||
extensionDir: string,
|
||||
): Promise<GeminiCLIExtension | null> {
|
||||
try {
|
||||
const stats = await fs.promises.stat(extensionDir);
|
||||
if (!stats.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -592,13 +665,6 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
|
||||
try {
|
||||
let config = await this.loadExtensionConfig(effectiveExtensionPath);
|
||||
if (
|
||||
this.getExtensions().find((extension) => extension.name === config.name)
|
||||
) {
|
||||
throw new Error(
|
||||
`Extension with name ${config.name} already was loaded.`,
|
||||
);
|
||||
}
|
||||
|
||||
const extensionId = getExtensionId(config, installMetadata);
|
||||
|
||||
@@ -768,7 +834,7 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
);
|
||||
}
|
||||
|
||||
const extension: GeminiCLIExtension = {
|
||||
return {
|
||||
name: config.name,
|
||||
version: config.version,
|
||||
path: effectiveExtensionPath,
|
||||
@@ -788,10 +854,6 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
agents: agentLoadResult.agents,
|
||||
themes: config.themes,
|
||||
};
|
||||
this.loadedExtensions = [...this.loadedExtensions, extension];
|
||||
|
||||
await this.maybeStartExtension(extension);
|
||||
return extension;
|
||||
} catch (e) {
|
||||
debugLogger.error(
|
||||
`Warning: Skipping extension in ${effectiveExtensionPath}: ${getErrorMessage(
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="207" viewBox="0 0 920 207">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="207" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Installing extension "test-ext". </text>
|
||||
<text x="0" y="19" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">This extension will run the following MCP servers: </text>
|
||||
<text x="0" y="36" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> * server1 (local): npm start </text>
|
||||
<text x="0" y="53" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> * server2 (remote): https://remote.com </text>
|
||||
<text x="0" y="70" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">This extension will append info to your gemini.md context using my-context.md </text>
|
||||
<text x="0" y="87" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">This extension will exclude the following core tools: tool1,tool2 </text>
|
||||
<text x="0" y="121" fill="#cdcd00" textLength="891" lengthAdjust="spacingAndGlyphs">The extension you are about to install may have been created by a third-party developer and sourced</text>
|
||||
<text x="0" y="138" fill="#cdcd00" textLength="882" lengthAdjust="spacingAndGlyphs">from a public repository. Google does not vet, endorse, or guarantee the functionality or security</text>
|
||||
<text x="0" y="155" fill="#cdcd00" textLength="846" lengthAdjust="spacingAndGlyphs">of extensions. Please carefully inspect any extension and its source code before installing to</text>
|
||||
<text x="0" y="172" fill="#cdcd00" textLength="630" lengthAdjust="spacingAndGlyphs">understand the permissions it requires and the actions it may perform.</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="139" viewBox="0 0 920 139">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="139" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Installing extension "test-ext". </text>
|
||||
<text x="0" y="19" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">⚠️ This extension contains Hooks which can automatically execute commands. </text>
|
||||
<text x="0" y="53" fill="#cdcd00" textLength="891" lengthAdjust="spacingAndGlyphs">The extension you are about to install may have been created by a third-party developer and sourced</text>
|
||||
<text x="0" y="70" fill="#cdcd00" textLength="882" lengthAdjust="spacingAndGlyphs">from a public repository. Google does not vet, endorse, or guarantee the functionality or security</text>
|
||||
<text x="0" y="87" fill="#cdcd00" textLength="846" lengthAdjust="spacingAndGlyphs">of extensions. Please carefully inspect any extension and its source code before installing to</text>
|
||||
<text x="0" y="104" fill="#cdcd00" textLength="630" lengthAdjust="spacingAndGlyphs">understand the permissions it requires and the actions it may perform.</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,28 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="479" viewBox="0 0 920 479">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="479" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Installing extension "test-ext". </text>
|
||||
<text x="0" y="19" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">This extension will run the following MCP servers: </text>
|
||||
<text x="0" y="36" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> * server1 (local): npm start </text>
|
||||
<text x="0" y="53" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> * server2 (remote): https://remote.com </text>
|
||||
<text x="0" y="70" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">This extension will append info to your gemini.md context using my-context.md </text>
|
||||
<text x="0" y="87" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">This extension will exclude the following core tools: tool1,tool2 </text>
|
||||
<text x="0" y="121" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Agent Skills: </text>
|
||||
<text x="0" y="155" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">This extension will install the following agent skills: </text>
|
||||
<text x="0" y="189" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> * skill1: desc1 </text>
|
||||
<text x="0" y="206" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> (Source: /mock/temp/dir/skill1/SKILL.md) (2 items in directory) </text>
|
||||
<text x="0" y="240" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> * skill2: desc2 </text>
|
||||
<text x="0" y="257" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> (Source: /mock/temp/dir/skill2/SKILL.md) (1 items in directory) </text>
|
||||
<text x="0" y="308" fill="#cdcd00" textLength="891" lengthAdjust="spacingAndGlyphs">The extension you are about to install may have been created by a third-party developer and sourced</text>
|
||||
<text x="0" y="325" fill="#cdcd00" textLength="882" lengthAdjust="spacingAndGlyphs">from a public repository. Google does not vet, endorse, or guarantee the functionality or security</text>
|
||||
<text x="0" y="342" fill="#cdcd00" textLength="846" lengthAdjust="spacingAndGlyphs">of extensions. Please carefully inspect any extension and its source code before installing to</text>
|
||||
<text x="0" y="359" fill="#cdcd00" textLength="630" lengthAdjust="spacingAndGlyphs">understand the permissions it requires and the actions it may perform.</text>
|
||||
<text x="0" y="393" fill="#cdcd00" textLength="882" lengthAdjust="spacingAndGlyphs">Agent skills inject specialized instructions and domain-specific knowledge into the agent's system</text>
|
||||
<text x="0" y="410" fill="#cdcd00" textLength="891" lengthAdjust="spacingAndGlyphs">prompt. This can change how the agent interprets your requests and interacts with your environment.</text>
|
||||
<text x="0" y="427" fill="#cdcd00" textLength="864" lengthAdjust="spacingAndGlyphs">Review the skill definitions at the location(s) provided below to ensure they meet your security</text>
|
||||
<text x="0" y="444" fill="#cdcd00" textLength="90" lengthAdjust="spacingAndGlyphs">standards.</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="343" viewBox="0 0 920 343">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="343" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Installing extension "test-ext". </text>
|
||||
<text x="0" y="36" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Agent Skills: </text>
|
||||
<text x="0" y="70" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">This extension will install the following agent skills: </text>
|
||||
<text x="0" y="104" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> * locked-skill: A skill in a locked dir </text>
|
||||
<text x="0" y="121" fill="#ffffff" textLength="405" lengthAdjust="spacingAndGlyphs"> (Source: /mock/temp/dir/locked/SKILL.md) </text>
|
||||
<text x="405" y="121" fill="#cd0000" textLength="342" lengthAdjust="spacingAndGlyphs">⚠️ (Could not count items in directory)</text>
|
||||
<text x="0" y="172" fill="#cdcd00" textLength="891" lengthAdjust="spacingAndGlyphs">The extension you are about to install may have been created by a third-party developer and sourced</text>
|
||||
<text x="0" y="189" fill="#cdcd00" textLength="882" lengthAdjust="spacingAndGlyphs">from a public repository. Google does not vet, endorse, or guarantee the functionality or security</text>
|
||||
<text x="0" y="206" fill="#cdcd00" textLength="846" lengthAdjust="spacingAndGlyphs">of extensions. Please carefully inspect any extension and its source code before installing to</text>
|
||||
<text x="0" y="223" fill="#cdcd00" textLength="630" lengthAdjust="spacingAndGlyphs">understand the permissions it requires and the actions it may perform.</text>
|
||||
<text x="0" y="257" fill="#cdcd00" textLength="882" lengthAdjust="spacingAndGlyphs">Agent skills inject specialized instructions and domain-specific knowledge into the agent's system</text>
|
||||
<text x="0" y="274" fill="#cdcd00" textLength="891" lengthAdjust="spacingAndGlyphs">prompt. This can change how the agent interprets your requests and interacts with your environment.</text>
|
||||
<text x="0" y="291" fill="#cdcd00" textLength="864" lengthAdjust="spacingAndGlyphs">Review the skill definitions at the location(s) provided below to ensure they meet your security</text>
|
||||
<text x="0" y="308" fill="#cdcd00" textLength="90" lengthAdjust="spacingAndGlyphs">standards.</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="241" viewBox="0 0 920 241">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="241" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Installing agent skill(s) from "https://example.com/repo.git". </text>
|
||||
<text x="0" y="36" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">The following agent skill(s) will be installing: </text>
|
||||
<text x="0" y="70" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> * skill1: desc1 </text>
|
||||
<text x="0" y="87" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> (Source: /mock/temp/dir/skill1/SKILL.md) (1 items in directory) </text>
|
||||
<text x="0" y="121" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Install Destination: /mock/target/dir </text>
|
||||
<text x="0" y="155" fill="#cdcd00" textLength="882" lengthAdjust="spacingAndGlyphs">Agent skills inject specialized instructions and domain-specific knowledge into the agent's system</text>
|
||||
<text x="0" y="172" fill="#cdcd00" textLength="891" lengthAdjust="spacingAndGlyphs">prompt. This can change how the agent interprets your requests and interacts with your environment.</text>
|
||||
<text x="0" y="189" fill="#cdcd00" textLength="864" lengthAdjust="spacingAndGlyphs">Review the skill definitions at the location(s) provided below to ensure they meet your security</text>
|
||||
<text x="0" y="206" fill="#cdcd00" textLength="90" lengthAdjust="spacingAndGlyphs">standards.</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,93 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`consent > maybeRequestConsentOrFail > consent string generation > should generate a consent string with all fields 1`] = `
|
||||
"Installing extension "test-ext".
|
||||
This extension will run the following MCP servers:
|
||||
* server1 (local): npm start
|
||||
* server2 (remote): https://remote.com
|
||||
This extension will append info to your gemini.md context using my-context.md
|
||||
This extension will exclude the following core tools: tool1,tool2
|
||||
|
||||
The extension you are about to install may have been created by a third-party developer and sourced
|
||||
from a public repository. Google does not vet, endorse, or guarantee the functionality or security
|
||||
of extensions. Please carefully inspect any extension and its source code before installing to
|
||||
understand the permissions it requires and the actions it may perform."
|
||||
`;
|
||||
|
||||
exports[`consent > maybeRequestConsentOrFail > consent string generation > should include warning when hooks are present 1`] = `
|
||||
"Installing extension "test-ext".
|
||||
⚠️ This extension contains Hooks which can automatically execute commands.
|
||||
|
||||
The extension you are about to install may have been created by a third-party developer and sourced
|
||||
from a public repository. Google does not vet, endorse, or guarantee the functionality or security
|
||||
of extensions. Please carefully inspect any extension and its source code before installing to
|
||||
understand the permissions it requires and the actions it may perform."
|
||||
`;
|
||||
|
||||
exports[`consent > maybeRequestConsentOrFail > consent string generation > should request consent if skills change 1`] = `
|
||||
"Installing extension "test-ext".
|
||||
This extension will run the following MCP servers:
|
||||
* server1 (local): npm start
|
||||
* server2 (remote): https://remote.com
|
||||
This extension will append info to your gemini.md context using my-context.md
|
||||
This extension will exclude the following core tools: tool1,tool2
|
||||
|
||||
Agent Skills:
|
||||
|
||||
This extension will install the following agent skills:
|
||||
|
||||
* skill1: desc1
|
||||
(Source: /mock/temp/dir/skill1/SKILL.md) (2 items in directory)
|
||||
|
||||
* skill2: desc2
|
||||
(Source: /mock/temp/dir/skill2/SKILL.md) (1 items in directory)
|
||||
|
||||
|
||||
The extension you are about to install may have been created by a third-party developer and sourced
|
||||
from a public repository. Google does not vet, endorse, or guarantee the functionality or security
|
||||
of extensions. Please carefully inspect any extension and its source code before installing to
|
||||
understand the permissions it requires and the actions it may perform.
|
||||
|
||||
Agent skills inject specialized instructions and domain-specific knowledge into the agent's system
|
||||
prompt. This can change how the agent interprets your requests and interacts with your environment.
|
||||
Review the skill definitions at the location(s) provided below to ensure they meet your security
|
||||
standards."
|
||||
`;
|
||||
|
||||
exports[`consent > maybeRequestConsentOrFail > consent string generation > should show a warning if the skill directory cannot be read 1`] = `
|
||||
"Installing extension "test-ext".
|
||||
|
||||
Agent Skills:
|
||||
|
||||
This extension will install the following agent skills:
|
||||
|
||||
* locked-skill: A skill in a locked dir
|
||||
(Source: /mock/temp/dir/locked/SKILL.md) ⚠️ (Could not count items in directory)
|
||||
|
||||
|
||||
The extension you are about to install may have been created by a third-party developer and sourced
|
||||
from a public repository. Google does not vet, endorse, or guarantee the functionality or security
|
||||
of extensions. Please carefully inspect any extension and its source code before installing to
|
||||
understand the permissions it requires and the actions it may perform.
|
||||
|
||||
Agent skills inject specialized instructions and domain-specific knowledge into the agent's system
|
||||
prompt. This can change how the agent interprets your requests and interacts with your environment.
|
||||
Review the skill definitions at the location(s) provided below to ensure they meet your security
|
||||
standards."
|
||||
`;
|
||||
|
||||
exports[`consent > skillsConsentString > should generate a consent string for skills 1`] = `
|
||||
"Installing agent skill(s) from "https://example.com/repo.git".
|
||||
|
||||
The following agent skill(s) will be installing:
|
||||
|
||||
* skill1: desc1
|
||||
(Source: /mock/temp/dir/skill1/SKILL.md) (1 items in directory)
|
||||
|
||||
Install Destination: /mock/target/dir
|
||||
|
||||
Agent skills inject specialized instructions and domain-specific knowledge into the agent's system
|
||||
prompt. This can change how the agent interprets your requests and interacts with your environment.
|
||||
Review the skill definitions at the location(s) provided below to ensure they meet your security
|
||||
standards."
|
||||
`;
|
||||
@@ -4,17 +4,17 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Text } from 'ink';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import chalk from 'chalk';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
import * as os from 'node:os';
|
||||
import { render, cleanup } from '../../test-utils/render.js';
|
||||
import {
|
||||
requestConsentNonInteractive,
|
||||
requestConsentInteractive,
|
||||
maybeRequestConsentOrFail,
|
||||
INSTALL_WARNING_MESSAGE,
|
||||
SKILLS_WARNING_MESSAGE,
|
||||
} from './consent.js';
|
||||
import type { ConfirmationRequest } from '../../ui/types.js';
|
||||
import type { ExtensionConfig } from '../extension.js';
|
||||
@@ -58,6 +58,21 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
async function expectConsentSnapshot(consentString: string) {
|
||||
const renderResult = render(React.createElement(Text, null, consentString));
|
||||
await renderResult.waitUntilReady();
|
||||
await expect(renderResult).toMatchSvgSnapshot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a consent string for snapshot testing by:
|
||||
* 1. Replacing the dynamic temp directory path with a static placeholder.
|
||||
* 2. Converting Windows backslashes to forward slashes for platform-agnosticism.
|
||||
*/
|
||||
function normalizePathsForSnapshot(str: string, tempDir: string): string {
|
||||
return str.replaceAll(tempDir, '/mock/temp/dir').replaceAll('\\', '/');
|
||||
}
|
||||
|
||||
describe('consent', () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -75,6 +90,7 @@ describe('consent', () => {
|
||||
if (tempDir) {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe('requestConsentNonInteractive', () => {
|
||||
@@ -189,18 +205,9 @@ describe('consent', () => {
|
||||
undefined,
|
||||
);
|
||||
|
||||
const expectedConsentString = [
|
||||
'Installing extension "test-ext".',
|
||||
'This extension will run the following MCP servers:',
|
||||
' * server1 (local): npm start',
|
||||
' * server2 (remote): https://remote.com',
|
||||
'This extension will append info to your gemini.md context using my-context.md',
|
||||
'This extension will exclude the following core tools: tool1,tool2',
|
||||
'',
|
||||
INSTALL_WARNING_MESSAGE,
|
||||
].join('\n');
|
||||
|
||||
expect(requestConsent).toHaveBeenCalledWith(expectedConsentString);
|
||||
expect(requestConsent).toHaveBeenCalledTimes(1);
|
||||
const consentString = requestConsent.mock.calls[0][0] as string;
|
||||
await expectConsentSnapshot(consentString);
|
||||
});
|
||||
|
||||
it('should request consent if mcpServers change', async () => {
|
||||
@@ -263,11 +270,9 @@ describe('consent', () => {
|
||||
undefined,
|
||||
);
|
||||
|
||||
expect(requestConsent).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
'⚠️ This extension contains Hooks which can automatically execute commands.',
|
||||
),
|
||||
);
|
||||
expect(requestConsent).toHaveBeenCalledTimes(1);
|
||||
const consentString = requestConsent.mock.calls[0][0] as string;
|
||||
await expectConsentSnapshot(consentString);
|
||||
});
|
||||
|
||||
it('should request consent if hooks status changes', async () => {
|
||||
@@ -323,29 +328,10 @@ describe('consent', () => {
|
||||
[skill1, skill2],
|
||||
);
|
||||
|
||||
const expectedConsentString = [
|
||||
'Installing extension "test-ext".',
|
||||
'This extension will run the following MCP servers:',
|
||||
' * server1 (local): npm start',
|
||||
' * server2 (remote): https://remote.com',
|
||||
'This extension will append info to your gemini.md context using my-context.md',
|
||||
'This extension will exclude the following core tools: tool1,tool2',
|
||||
'',
|
||||
chalk.bold('Agent Skills:'),
|
||||
'\nThis extension will install the following agent skills:\n',
|
||||
` * ${chalk.bold('skill1')}: desc1`,
|
||||
chalk.dim(` (Source: ${skill1.location}) (2 items in directory)`),
|
||||
'',
|
||||
` * ${chalk.bold('skill2')}: desc2`,
|
||||
chalk.dim(` (Source: ${skill2.location}) (1 items in directory)`),
|
||||
'',
|
||||
'',
|
||||
INSTALL_WARNING_MESSAGE,
|
||||
'',
|
||||
SKILLS_WARNING_MESSAGE,
|
||||
].join('\n');
|
||||
|
||||
expect(requestConsent).toHaveBeenCalledWith(expectedConsentString);
|
||||
expect(requestConsent).toHaveBeenCalledTimes(1);
|
||||
let consentString = requestConsent.mock.calls[0][0] as string;
|
||||
consentString = normalizePathsForSnapshot(consentString, tempDir);
|
||||
await expectConsentSnapshot(consentString);
|
||||
});
|
||||
|
||||
it('should show a warning if the skill directory cannot be read', async () => {
|
||||
@@ -377,11 +363,10 @@ describe('consent', () => {
|
||||
[skill],
|
||||
);
|
||||
|
||||
expect(requestConsent).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
` (Source: ${skill.location}) ${chalk.red('⚠️ (Could not count items in directory)')}`,
|
||||
),
|
||||
);
|
||||
expect(requestConsent).toHaveBeenCalledTimes(1);
|
||||
let consentString = requestConsent.mock.calls[0][0] as string;
|
||||
consentString = normalizePathsForSnapshot(consentString, tempDir);
|
||||
await expectConsentSnapshot(consentString);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -400,21 +385,14 @@ describe('consent', () => {
|
||||
};
|
||||
|
||||
const { skillsConsentString } = await import('./consent.js');
|
||||
const consentString = await skillsConsentString(
|
||||
let consentString = await skillsConsentString(
|
||||
[skill1],
|
||||
'https://example.com/repo.git',
|
||||
'/mock/target/dir',
|
||||
);
|
||||
|
||||
expect(consentString).toContain(
|
||||
'Installing agent skill(s) from "https://example.com/repo.git".',
|
||||
);
|
||||
expect(consentString).toContain('Install Destination: /mock/target/dir');
|
||||
expect(consentString).toContain('\n' + SKILLS_WARNING_MESSAGE);
|
||||
expect(consentString).toContain(` * ${chalk.bold('skill1')}: desc1`);
|
||||
expect(consentString).toContain(
|
||||
chalk.dim(`(Source: ${skill1.location}) (1 items in directory)`),
|
||||
);
|
||||
consentString = normalizePathsForSnapshot(consentString, tempDir);
|
||||
await expectConsentSnapshot(consentString);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,7 +8,11 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import * as os from 'node:os';
|
||||
import { resolveWorkspacePolicyState } from './policy.js';
|
||||
import {
|
||||
resolveWorkspacePolicyState,
|
||||
autoAcceptWorkspacePolicies,
|
||||
setAutoAcceptWorkspacePolicies,
|
||||
} from './policy.js';
|
||||
import { writeToStderr } from '@google/gemini-cli-core';
|
||||
|
||||
// Mock debugLogger to avoid noise in test output
|
||||
@@ -68,24 +72,18 @@ describe('resolveWorkspacePolicyState', () => {
|
||||
fs.mkdirSync(policiesDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(policiesDir, 'policy.toml'), 'rules = []');
|
||||
|
||||
// First call to establish integrity (interactive accept)
|
||||
// First call to establish integrity (interactive auto-accept)
|
||||
const firstResult = await resolveWorkspacePolicyState({
|
||||
cwd: workspaceDir,
|
||||
trustedFolder: true,
|
||||
interactive: true,
|
||||
});
|
||||
expect(firstResult.policyUpdateConfirmationRequest).toBeDefined();
|
||||
|
||||
// Establish integrity manually as if accepted
|
||||
const { PolicyIntegrityManager } = await import('@google/gemini-cli-core');
|
||||
const integrityManager = new PolicyIntegrityManager();
|
||||
await integrityManager.acceptIntegrity(
|
||||
'workspace',
|
||||
workspaceDir,
|
||||
firstResult.policyUpdateConfirmationRequest!.newHash,
|
||||
);
|
||||
expect(firstResult.workspacePoliciesDir).toBe(policiesDir);
|
||||
expect(firstResult.policyUpdateConfirmationRequest).toBeUndefined();
|
||||
expect(writeToStderr).not.toHaveBeenCalled();
|
||||
|
||||
// Second call should match
|
||||
|
||||
const result = await resolveWorkspacePolicyState({
|
||||
cwd: workspaceDir,
|
||||
trustedFolder: true,
|
||||
@@ -107,26 +105,33 @@ describe('resolveWorkspacePolicyState', () => {
|
||||
expect(result.policyUpdateConfirmationRequest).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return confirmation request if changed in interactive mode', async () => {
|
||||
fs.mkdirSync(policiesDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(policiesDir, 'policy.toml'), 'rules = []');
|
||||
it('should return confirmation request if changed in interactive mode when AUTO_ACCEPT is false', async () => {
|
||||
const originalValue = autoAcceptWorkspacePolicies;
|
||||
setAutoAcceptWorkspacePolicies(false);
|
||||
|
||||
const result = await resolveWorkspacePolicyState({
|
||||
cwd: workspaceDir,
|
||||
trustedFolder: true,
|
||||
interactive: true,
|
||||
});
|
||||
try {
|
||||
fs.mkdirSync(policiesDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(policiesDir, 'policy.toml'), 'rules = []');
|
||||
|
||||
expect(result.workspacePoliciesDir).toBeUndefined();
|
||||
expect(result.policyUpdateConfirmationRequest).toEqual({
|
||||
scope: 'workspace',
|
||||
identifier: workspaceDir,
|
||||
policyDir: policiesDir,
|
||||
newHash: expect.any(String),
|
||||
});
|
||||
const result = await resolveWorkspacePolicyState({
|
||||
cwd: workspaceDir,
|
||||
trustedFolder: true,
|
||||
interactive: true,
|
||||
});
|
||||
|
||||
expect(result.workspacePoliciesDir).toBeUndefined();
|
||||
expect(result.policyUpdateConfirmationRequest).toEqual({
|
||||
scope: 'workspace',
|
||||
identifier: workspaceDir,
|
||||
policyDir: policiesDir,
|
||||
newHash: expect.any(String),
|
||||
});
|
||||
} finally {
|
||||
setAutoAcceptWorkspacePolicies(originalValue);
|
||||
}
|
||||
});
|
||||
|
||||
it('should warn and auto-accept if changed in non-interactive mode', async () => {
|
||||
it('should warn and auto-accept if changed in non-interactive mode when AUTO_ACCEPT is true', async () => {
|
||||
fs.mkdirSync(policiesDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(policiesDir, 'policy.toml'), 'rules = []');
|
||||
|
||||
@@ -143,6 +148,30 @@ describe('resolveWorkspacePolicyState', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn and auto-accept if changed in non-interactive mode when AUTO_ACCEPT is false', async () => {
|
||||
const originalValue = autoAcceptWorkspacePolicies;
|
||||
setAutoAcceptWorkspacePolicies(false);
|
||||
|
||||
try {
|
||||
fs.mkdirSync(policiesDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(policiesDir, 'policy.toml'), 'rules = []');
|
||||
|
||||
const result = await resolveWorkspacePolicyState({
|
||||
cwd: workspaceDir,
|
||||
trustedFolder: true,
|
||||
interactive: false,
|
||||
});
|
||||
|
||||
expect(result.workspacePoliciesDir).toBe(policiesDir);
|
||||
expect(result.policyUpdateConfirmationRequest).toBeUndefined();
|
||||
expect(writeToStderr).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Automatically accepting and loading'),
|
||||
);
|
||||
} finally {
|
||||
setAutoAcceptWorkspacePolicies(originalValue);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not return workspace policies if cwd is the home directory', async () => {
|
||||
const policiesDir = path.join(tempDir, '.gemini', 'policies');
|
||||
fs.mkdirSync(policiesDir, { recursive: true });
|
||||
|
||||
@@ -17,9 +17,24 @@ import {
|
||||
Storage,
|
||||
type PolicyUpdateConfirmationRequest,
|
||||
writeToStderr,
|
||||
debugLogger,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { type Settings } from './settings.js';
|
||||
|
||||
/**
|
||||
* Temporary flag to automatically accept workspace policies to reduce friction.
|
||||
* Exported as 'let' to allow monkey patching in tests via the setter.
|
||||
*/
|
||||
export let autoAcceptWorkspacePolicies = true;
|
||||
|
||||
/**
|
||||
* Sets the autoAcceptWorkspacePolicies flag.
|
||||
* Used primarily for testing purposes.
|
||||
*/
|
||||
export function setAutoAcceptWorkspacePolicies(value: boolean) {
|
||||
autoAcceptWorkspacePolicies = value;
|
||||
}
|
||||
|
||||
export async function createPolicyEngineConfig(
|
||||
settings: Settings,
|
||||
approvalMode: ApprovalMode,
|
||||
@@ -91,8 +106,8 @@ export async function resolveWorkspacePolicyState(options: {
|
||||
) {
|
||||
// No workspace policies found
|
||||
workspacePoliciesDir = undefined;
|
||||
} else if (interactive) {
|
||||
// Policies changed or are new, and we are in interactive mode
|
||||
} else if (interactive && !autoAcceptWorkspacePolicies) {
|
||||
// Policies changed or are new, and we are in interactive mode and auto-accept is disabled
|
||||
policyUpdateConfirmationRequest = {
|
||||
scope: 'workspace',
|
||||
identifier: cwd,
|
||||
@@ -100,17 +115,23 @@ export async function resolveWorkspacePolicyState(options: {
|
||||
newHash: integrityResult.hash,
|
||||
};
|
||||
} else {
|
||||
// Non-interactive mode: warn and automatically accept/load
|
||||
// Non-interactive mode or auto-accept is enabled: automatically accept/load
|
||||
await integrityManager.acceptIntegrity(
|
||||
'workspace',
|
||||
cwd,
|
||||
integrityResult.hash,
|
||||
);
|
||||
workspacePoliciesDir = potentialWorkspacePoliciesDir;
|
||||
// debugLogger.warn here doesn't show up in the terminal. It is showing up only in debug mode on the debug console
|
||||
writeToStderr(
|
||||
'WARNING: Workspace policies changed or are new. Automatically accepting and loading them in non-interactive mode.\n',
|
||||
);
|
||||
|
||||
if (!interactive) {
|
||||
writeToStderr(
|
||||
'WARNING: Workspace policies changed or are new. Automatically accepting and loading them.\n',
|
||||
);
|
||||
} else {
|
||||
debugLogger.warn(
|
||||
'Workspace policies changed or are new. Automatically accepting and loading them.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -844,7 +844,7 @@ const SETTINGS_SCHEMA = {
|
||||
requiresRestart: false,
|
||||
default: undefined as string | undefined,
|
||||
description: 'The Gemini model to use for conversations.',
|
||||
showInDialog: false,
|
||||
showInDialog: true,
|
||||
},
|
||||
maxSessionTurns: {
|
||||
type: 'number',
|
||||
|
||||
@@ -10,6 +10,7 @@ import { loadCliConfig, type CliArgs } from './config.js';
|
||||
import { createTestMergedSettings } from './settings.js';
|
||||
import * as ServerConfig from '@google/gemini-cli-core';
|
||||
import { isWorkspaceTrusted } from './trustedFolders.js';
|
||||
import * as Policy from './policy.js';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('./trustedFolders.js', () => ({
|
||||
@@ -164,7 +165,7 @@ describe('Workspace-Level Policy CLI Integration', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should set policyUpdateConfirmationRequest if integrity MISMATCH in interactive mode', async () => {
|
||||
it('should automatically accept and load workspacePoliciesDir if integrity MISMATCH in interactive mode when AUTO_ACCEPT is true', async () => {
|
||||
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||
isTrusted: true,
|
||||
source: 'file',
|
||||
@@ -186,24 +187,23 @@ describe('Workspace-Level Policy CLI Integration', () => {
|
||||
cwd: MOCK_CWD,
|
||||
});
|
||||
|
||||
expect(config.getPolicyUpdateConfirmationRequest()).toEqual({
|
||||
scope: 'workspace',
|
||||
identifier: MOCK_CWD,
|
||||
policyDir: expect.stringContaining(path.join('.gemini', 'policies')),
|
||||
newHash: 'new-hash',
|
||||
});
|
||||
// In interactive mode without accept flag, it waits for user confirmation (handled by UI),
|
||||
// so it currently DOES NOT pass the directory to createPolicyEngineConfig yet.
|
||||
// The UI will handle the confirmation and reload/update.
|
||||
expect(config.getPolicyUpdateConfirmationRequest()).toBeUndefined();
|
||||
expect(mockAcceptIntegrity).toHaveBeenCalledWith(
|
||||
'workspace',
|
||||
MOCK_CWD,
|
||||
'new-hash',
|
||||
);
|
||||
expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
workspacePoliciesDir: undefined,
|
||||
workspacePoliciesDir: expect.stringContaining(
|
||||
path.join('.gemini', 'policies'),
|
||||
),
|
||||
}),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should set policyUpdateConfirmationRequest if integrity is NEW with files (first time seen) in interactive mode', async () => {
|
||||
it('should automatically accept and load workspacePoliciesDir if integrity is NEW in interactive mode when AUTO_ACCEPT is true', async () => {
|
||||
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||
isTrusted: true,
|
||||
source: 'file',
|
||||
@@ -222,18 +222,65 @@ describe('Workspace-Level Policy CLI Integration', () => {
|
||||
cwd: MOCK_CWD,
|
||||
});
|
||||
|
||||
expect(config.getPolicyUpdateConfirmationRequest()).toEqual({
|
||||
scope: 'workspace',
|
||||
identifier: MOCK_CWD,
|
||||
policyDir: expect.stringContaining(path.join('.gemini', 'policies')),
|
||||
newHash: 'new-hash',
|
||||
});
|
||||
expect(config.getPolicyUpdateConfirmationRequest()).toBeUndefined();
|
||||
expect(mockAcceptIntegrity).toHaveBeenCalledWith(
|
||||
'workspace',
|
||||
MOCK_CWD,
|
||||
'new-hash',
|
||||
);
|
||||
|
||||
expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
workspacePoliciesDir: undefined,
|
||||
workspacePoliciesDir: expect.stringContaining(
|
||||
path.join('.gemini', 'policies'),
|
||||
),
|
||||
}),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should set policyUpdateConfirmationRequest if integrity MISMATCH in interactive mode when AUTO_ACCEPT is false', async () => {
|
||||
// Monkey patch autoAcceptWorkspacePolicies using setter
|
||||
const originalValue = Policy.autoAcceptWorkspacePolicies;
|
||||
Policy.setAutoAcceptWorkspacePolicies(false);
|
||||
|
||||
try {
|
||||
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||
isTrusted: true,
|
||||
source: 'file',
|
||||
});
|
||||
mockCheckIntegrity.mockResolvedValue({
|
||||
status: 'mismatch',
|
||||
hash: 'new-hash',
|
||||
fileCount: 1,
|
||||
});
|
||||
vi.mocked(ServerConfig.isHeadlessMode).mockReturnValue(false); // Interactive
|
||||
|
||||
const settings = createTestMergedSettings();
|
||||
const argv = {
|
||||
query: 'test',
|
||||
promptInteractive: 'test',
|
||||
} as unknown as CliArgs;
|
||||
|
||||
const config = await loadCliConfig(settings, 'test-session', argv, {
|
||||
cwd: MOCK_CWD,
|
||||
});
|
||||
|
||||
expect(config.getPolicyUpdateConfirmationRequest()).toEqual({
|
||||
scope: 'workspace',
|
||||
identifier: MOCK_CWD,
|
||||
policyDir: expect.stringContaining(path.join('.gemini', 'policies')),
|
||||
newHash: 'new-hash',
|
||||
});
|
||||
expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
workspacePoliciesDir: undefined,
|
||||
}),
|
||||
expect.anything(),
|
||||
);
|
||||
} finally {
|
||||
// Restore for other tests
|
||||
Policy.setAutoAcceptWorkspacePolicies(originalValue);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
createContentGenerator,
|
||||
IdeClient,
|
||||
debugLogger,
|
||||
CoreToolCallStatus,
|
||||
} from '@google/gemini-cli-core';
|
||||
import {
|
||||
type MockShellCommand,
|
||||
@@ -36,7 +37,47 @@ import {
|
||||
} from './MockShellExecutionService.js';
|
||||
import { createMockSettings } from './settings.js';
|
||||
import { type LoadedSettings } from '../config/settings.js';
|
||||
import { AuthState } from '../ui/types.js';
|
||||
import { AuthState, StreamingState } from '../ui/types.js';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import type {
|
||||
TrackedCancelledToolCall,
|
||||
TrackedCompletedToolCall,
|
||||
TrackedToolCall,
|
||||
} from '../ui/hooks/useToolScheduler.js';
|
||||
|
||||
// Global state observer for React-based signals
|
||||
const sessionStateMap = new Map<string, StreamingState>();
|
||||
const activeRigs = new Map<string, AppRig>();
|
||||
|
||||
// Mock StreamingContext to report state changes back to the observer
|
||||
vi.mock('../ui/contexts/StreamingContext.js', async (importOriginal) => {
|
||||
const original =
|
||||
await importOriginal<typeof import('../ui/contexts/StreamingContext.js')>();
|
||||
const { useConfig } = await import('../ui/contexts/ConfigContext.js');
|
||||
const React = await import('react');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useStreamingContext: () => {
|
||||
const state = original.useStreamingContext();
|
||||
const config = useConfig();
|
||||
const sessionId = config.getSessionId();
|
||||
|
||||
React.useEffect(() => {
|
||||
sessionStateMap.set(sessionId, state);
|
||||
// If we see activity, we are no longer "awaiting" the start of a response
|
||||
if (state !== StreamingState.Idle) {
|
||||
const rig = activeRigs.get(sessionId);
|
||||
if (rig) {
|
||||
rig.awaitingResponse = false;
|
||||
}
|
||||
}
|
||||
}, [sessionId, state]);
|
||||
|
||||
return state;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Mock core functions globally for tests using AppRig.
|
||||
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
@@ -112,9 +153,18 @@ export class AppRig {
|
||||
private breakpointTools = new Set<string | undefined>();
|
||||
private lastAwaitedConfirmation: PendingConfirmation | undefined;
|
||||
|
||||
/**
|
||||
* True if a message was just sent but React hasn't yet reported a non-idle state.
|
||||
*/
|
||||
awaitingResponse = false;
|
||||
|
||||
constructor(private options: AppRigOptions = {}) {
|
||||
this.testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gemini-app-rig-'));
|
||||
this.sessionId = `test-session-${Math.random().toString(36).slice(2, 9)}`;
|
||||
const uniqueId = randomUUID();
|
||||
this.testDir = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), `gemini-app-rig-${uniqueId.slice(0, 8)}-`),
|
||||
);
|
||||
this.sessionId = `test-session-${uniqueId}`;
|
||||
activeRigs.set(this.sessionId, this);
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
@@ -245,6 +295,8 @@ export class AppRig {
|
||||
};
|
||||
}
|
||||
|
||||
private toolCalls: TrackedToolCall[] = [];
|
||||
|
||||
private setupMessageBusListeners() {
|
||||
if (!this.config) return;
|
||||
const messageBus = this.config.getMessageBus();
|
||||
@@ -252,6 +304,7 @@ export class AppRig {
|
||||
messageBus.subscribe(
|
||||
MessageBusType.TOOL_CALLS_UPDATE,
|
||||
(message: ToolCallsUpdateMessage) => {
|
||||
this.toolCalls = message.toolCalls;
|
||||
for (const call of message.toolCalls) {
|
||||
if (call.status === 'awaiting_approval' && call.correlationId) {
|
||||
const details = call.confirmationDetails;
|
||||
@@ -281,6 +334,48 @@ export class AppRig {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the agent is currently busy (responding or executing tools).
|
||||
*/
|
||||
isBusy(): boolean {
|
||||
if (this.awaitingResponse) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const reactState = sessionStateMap.get(this.sessionId);
|
||||
// If we have a React-based state, use it as the definitive signal.
|
||||
// 'responding' and 'waiting-for-confirmation' both count as busy for the overall task.
|
||||
if (reactState !== undefined) {
|
||||
return reactState !== StreamingState.Idle;
|
||||
}
|
||||
|
||||
// Fallback to tool tracking if React hasn't reported yet
|
||||
const isAnyToolActive = this.toolCalls.some((tc) => {
|
||||
if (
|
||||
tc.status === CoreToolCallStatus.Executing ||
|
||||
tc.status === CoreToolCallStatus.Scheduled ||
|
||||
tc.status === CoreToolCallStatus.Validating
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
tc.status === CoreToolCallStatus.Success ||
|
||||
tc.status === CoreToolCallStatus.Error ||
|
||||
tc.status === CoreToolCallStatus.Cancelled
|
||||
) {
|
||||
return !(tc as TrackedCompletedToolCall | TrackedCancelledToolCall)
|
||||
.responseSubmittedToGemini;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const isAwaitingConfirmation = this.toolCalls.some(
|
||||
(tc) => tc.status === CoreToolCallStatus.AwaitingApproval,
|
||||
);
|
||||
|
||||
return isAnyToolActive || isAwaitingConfirmation;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.config || !this.settings)
|
||||
throw new Error('AppRig not initialized');
|
||||
@@ -334,17 +429,21 @@ export class AppRig {
|
||||
this.setBreakpoint(name);
|
||||
}
|
||||
} else {
|
||||
this.setToolPolicy(toolName, PolicyDecision.ASK_USER, 100);
|
||||
// Use undefined toolName to create a global rule if '*' is provided
|
||||
const actualToolName = toolName === '*' ? undefined : toolName;
|
||||
this.setToolPolicy(actualToolName, PolicyDecision.ASK_USER, 100);
|
||||
this.breakpointTools.add(toolName);
|
||||
}
|
||||
}
|
||||
|
||||
removeToolPolicy(toolName?: string, source = 'AppRig Override') {
|
||||
if (!this.config) throw new Error('AppRig not initialized');
|
||||
// Map '*' back to undefined for policy removal
|
||||
const actualToolName = toolName === '*' ? undefined : toolName;
|
||||
this.config
|
||||
.getPolicyEngine()
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
.removeRulesForTool(toolName as string, source);
|
||||
.removeRulesForTool(actualToolName as string, source);
|
||||
this.breakpointTools.delete(toolName);
|
||||
}
|
||||
|
||||
@@ -416,6 +515,44 @@ export class AppRig {
|
||||
return matched!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for either a tool confirmation request OR for the agent to go idle.
|
||||
*/
|
||||
async waitForNextEvent(
|
||||
timeout = 60000,
|
||||
): Promise<
|
||||
| { type: 'confirmation'; confirmation: PendingConfirmation }
|
||||
| { type: 'idle' }
|
||||
> {
|
||||
let confirmation: PendingConfirmation | undefined;
|
||||
let isIdle = false;
|
||||
|
||||
await this.waitUntil(
|
||||
async () => {
|
||||
await act(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
});
|
||||
confirmation = this.getPendingConfirmations()[0];
|
||||
// Now that we have a code-powered signal, this should be perfectly deterministic.
|
||||
isIdle = !this.isBusy();
|
||||
return !!confirmation || isIdle;
|
||||
},
|
||||
{
|
||||
timeout,
|
||||
message: 'Timed out waiting for next event (confirmation or idle).',
|
||||
},
|
||||
);
|
||||
|
||||
if (confirmation) {
|
||||
this.lastAwaitedConfirmation = confirmation;
|
||||
return { type: 'confirmation', confirmation };
|
||||
}
|
||||
|
||||
// Ensure all renders are flushed before returning 'idle'
|
||||
await this.renderResult?.waitUntilReady();
|
||||
return { type: 'idle' };
|
||||
}
|
||||
|
||||
async resolveTool(
|
||||
toolNameOrDisplayName: string | RegExp | PendingConfirmation,
|
||||
outcome: ToolConfirmationOutcome = ToolConfirmationOutcome.ProceedOnce,
|
||||
@@ -471,6 +608,32 @@ export class AppRig {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Drains all pending tool calls that hit a breakpoint until the agent is idle.
|
||||
* Useful for negative tests to ensure no unwanted tools (like generalist) are called.
|
||||
*
|
||||
* @param onConfirmation Optional callback to inspect each confirmation before resolving.
|
||||
* Return true to skip the default resolveTool call (e.g. if you handled it).
|
||||
*/
|
||||
async drainBreakpointsUntilIdle(
|
||||
onConfirmation?: (confirmation: PendingConfirmation) => void | boolean,
|
||||
timeout = 60000,
|
||||
) {
|
||||
while (true) {
|
||||
const event = await this.waitForNextEvent(timeout);
|
||||
if (event.type === 'idle') {
|
||||
break;
|
||||
}
|
||||
|
||||
const confirmation = event.confirmation;
|
||||
const handled = onConfirmation?.(confirmation);
|
||||
|
||||
if (!handled) {
|
||||
await this.resolveTool(confirmation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getConfig(): Config {
|
||||
if (!this.config) throw new Error('AppRig not initialized');
|
||||
return this.config;
|
||||
@@ -530,11 +693,16 @@ export class AppRig {
|
||||
}
|
||||
|
||||
async sendMessage(text: string) {
|
||||
this.awaitingResponse = true;
|
||||
await this.type(text);
|
||||
await this.pressEnter();
|
||||
}
|
||||
|
||||
async unmount() {
|
||||
// Clean up global state for this session
|
||||
sessionStateMap.delete(this.sessionId);
|
||||
activeRigs.delete(this.sessionId);
|
||||
|
||||
// Poison the chat recording service to prevent late writes to the test directory
|
||||
if (this.config) {
|
||||
const recordingService = this.config
|
||||
|
||||
@@ -6,20 +6,78 @@
|
||||
|
||||
/// <reference types="vitest/globals" />
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { Assertion } from 'vitest';
|
||||
import { expect } from 'vitest';
|
||||
import { expect, type Assertion } from 'vitest';
|
||||
import path from 'node:path';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import type { TextBuffer } from '../ui/components/shared/text-buffer.js';
|
||||
|
||||
// RegExp to detect invalid characters: backspace, and ANSI escape codes
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const invalidCharsRegex = /[\b\x1b]/;
|
||||
|
||||
const callCountByTest = new Map<string, number>();
|
||||
|
||||
export async function toMatchSvgSnapshot(
|
||||
this: Assertion,
|
||||
renderInstance: {
|
||||
lastFrameRaw?: (options?: { allowEmpty?: boolean }) => string;
|
||||
lastFrame?: (options?: { allowEmpty?: boolean }) => string;
|
||||
generateSvg: () => string;
|
||||
},
|
||||
options?: { allowEmpty?: boolean; name?: string },
|
||||
) {
|
||||
const currentTestName = expect.getState().currentTestName;
|
||||
if (!currentTestName) {
|
||||
throw new Error('toMatchSvgSnapshot must be called within a test');
|
||||
}
|
||||
const testPath = expect.getState().testPath;
|
||||
if (!testPath) {
|
||||
throw new Error('toMatchSvgSnapshot requires testPath');
|
||||
}
|
||||
|
||||
let textContent: string;
|
||||
if (renderInstance.lastFrameRaw) {
|
||||
textContent = renderInstance.lastFrameRaw({
|
||||
allowEmpty: options?.allowEmpty,
|
||||
});
|
||||
} else if (renderInstance.lastFrame) {
|
||||
textContent = renderInstance.lastFrame({ allowEmpty: options?.allowEmpty });
|
||||
} else {
|
||||
throw new Error(
|
||||
'toMatchSvgSnapshot requires a renderInstance with either lastFrameRaw or lastFrame',
|
||||
);
|
||||
}
|
||||
const svgContent = renderInstance.generateSvg();
|
||||
|
||||
const sanitize = (name: string) =>
|
||||
name.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-');
|
||||
|
||||
const testId = testPath + ':' + currentTestName;
|
||||
let count = callCountByTest.get(testId) ?? 0;
|
||||
count++;
|
||||
callCountByTest.set(testId, count);
|
||||
|
||||
const snapshotName =
|
||||
options?.name ??
|
||||
(count > 1 ? `${currentTestName}-${count}` : currentTestName);
|
||||
|
||||
const svgFileName =
|
||||
sanitize(path.basename(testPath).replace(/\.test\.tsx?$/, '')) +
|
||||
'-' +
|
||||
sanitize(snapshotName) +
|
||||
'.snap.svg';
|
||||
const svgDir = path.join(path.dirname(testPath), '__snapshots__');
|
||||
const svgFilePath = path.join(svgDir, svgFileName);
|
||||
|
||||
// Assert the text matches standard snapshot, stripping ANSI for stability
|
||||
expect(stripAnsi(textContent)).toMatchSnapshot();
|
||||
|
||||
// Assert the SVG matches the file snapshot
|
||||
await expect(svgContent).toMatchFileSnapshot(svgFilePath);
|
||||
|
||||
return { pass: true, message: () => '' };
|
||||
}
|
||||
|
||||
function toHaveOnlyValidCharacters(this: Assertion, buffer: TextBuffer) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-assignment
|
||||
const { isNot } = this as any;
|
||||
@@ -53,15 +111,22 @@ function toHaveOnlyValidCharacters(this: Assertion, buffer: TextBuffer) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
expect.extend({
|
||||
toHaveOnlyValidCharacters,
|
||||
toMatchSvgSnapshot,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any);
|
||||
|
||||
// Extend Vitest's `expect` interface with the custom matcher's type definition.
|
||||
declare module 'vitest' {
|
||||
interface Assertion<T> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type
|
||||
interface Assertion<T = any> extends CustomMatchers<T> {}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
interface AsymmetricMatchersContaining extends CustomMatchers {}
|
||||
|
||||
interface CustomMatchers<T = unknown> {
|
||||
toHaveOnlyValidCharacters(): T;
|
||||
}
|
||||
interface AsymmetricMatchersContaining {
|
||||
toHaveOnlyValidCharacters(): void;
|
||||
toMatchSvgSnapshot(options?: {
|
||||
allowEmpty?: boolean;
|
||||
name?: string;
|
||||
}): Promise<void>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { vi } from 'vitest';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import { format } from 'node:util';
|
||||
|
||||
export function createMockDebugLogger(options: { stripAnsi?: boolean } = {}) {
|
||||
const emitConsoleLog = vi.fn();
|
||||
const debugLogger = {
|
||||
log: vi.fn((message: unknown, ...args: unknown[]) => {
|
||||
let formatted =
|
||||
typeof message === 'string' ? format(message, ...args) : message;
|
||||
if (options.stripAnsi && typeof formatted === 'string') {
|
||||
formatted = stripAnsi(formatted);
|
||||
}
|
||||
emitConsoleLog('log', formatted);
|
||||
}),
|
||||
error: vi.fn((message: unknown, ...args: unknown[]) => {
|
||||
let formatted =
|
||||
typeof message === 'string' ? format(message, ...args) : message;
|
||||
if (options.stripAnsi && typeof formatted === 'string') {
|
||||
formatted = stripAnsi(formatted);
|
||||
}
|
||||
emitConsoleLog('error', formatted);
|
||||
}),
|
||||
warn: vi.fn((message: unknown, ...args: unknown[]) => {
|
||||
let formatted =
|
||||
typeof message === 'string' ? format(message, ...args) : message;
|
||||
if (options.stripAnsi && typeof formatted === 'string') {
|
||||
formatted = stripAnsi(formatted);
|
||||
}
|
||||
emitConsoleLog('warn', formatted);
|
||||
}),
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
};
|
||||
|
||||
return { emitConsoleLog, debugLogger };
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper specifically designed for `vi.mock('@google/gemini-cli-core', ...)` to easily
|
||||
* mock both `debugLogger` and `coreEvents.emitConsoleLog`.
|
||||
*
|
||||
* Example:
|
||||
* ```typescript
|
||||
* vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
* const { mockCoreDebugLogger } = await import('../../test-utils/mockDebugLogger.js');
|
||||
* return mockCoreDebugLogger(
|
||||
* await importOriginal<typeof import('@google/gemini-cli-core')>(),
|
||||
* { stripAnsi: true }
|
||||
* );
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function mockCoreDebugLogger<T extends Record<string, unknown>>(
|
||||
actual: T,
|
||||
options?: { stripAnsi?: boolean },
|
||||
): T {
|
||||
const { emitConsoleLog, debugLogger } = createMockDebugLogger(options);
|
||||
return {
|
||||
...actual,
|
||||
coreEvents: {
|
||||
...(typeof actual['coreEvents'] === 'object' &&
|
||||
actual['coreEvents'] !== null
|
||||
? actual['coreEvents']
|
||||
: {}),
|
||||
emitConsoleLog,
|
||||
},
|
||||
debugLogger,
|
||||
} as T;
|
||||
}
|
||||
@@ -51,6 +51,7 @@ import { SessionStatsProvider } from '../ui/contexts/SessionContext.js';
|
||||
import { themeManager, DEFAULT_THEME } from '../ui/themes/theme-manager.js';
|
||||
import { DefaultLight } from '../ui/themes/default-light.js';
|
||||
import { pickDefaultThemeName } from '../ui/themes/theme.js';
|
||||
import { generateSvgForTerminal } from './svg.js';
|
||||
|
||||
export const persistentStateMock = new FakePersistentState();
|
||||
|
||||
@@ -105,7 +106,12 @@ class XtermStdout extends EventEmitter {
|
||||
private queue: { promise: Promise<void> };
|
||||
isTTY = true;
|
||||
|
||||
getColorDepth(): number {
|
||||
return 24;
|
||||
}
|
||||
|
||||
private lastRenderOutput: string | undefined = undefined;
|
||||
private lastRenderStaticContent: string | undefined = undefined;
|
||||
|
||||
constructor(state: TerminalState, queue: { promise: Promise<void> }) {
|
||||
super();
|
||||
@@ -138,6 +144,7 @@ class XtermStdout extends EventEmitter {
|
||||
clear = () => {
|
||||
this.state.terminal.reset();
|
||||
this.lastRenderOutput = undefined;
|
||||
this.lastRenderStaticContent = undefined;
|
||||
};
|
||||
|
||||
dispose = () => {
|
||||
@@ -146,10 +153,32 @@ class XtermStdout extends EventEmitter {
|
||||
|
||||
onRender = (staticContent: string, output: string) => {
|
||||
this.renderCount++;
|
||||
this.lastRenderStaticContent = staticContent;
|
||||
this.lastRenderOutput = output;
|
||||
this.emit('render');
|
||||
};
|
||||
|
||||
private normalizeFrame = (text: string): string =>
|
||||
text.replace(/\r\n/g, '\n');
|
||||
|
||||
generateSvg = (): string => generateSvgForTerminal(this.state.terminal);
|
||||
|
||||
lastFrameRaw = (options: { allowEmpty?: boolean } = {}) => {
|
||||
const result =
|
||||
(this.lastRenderStaticContent ?? '') + (this.lastRenderOutput ?? '');
|
||||
|
||||
const normalized = this.normalizeFrame(result);
|
||||
|
||||
if (normalized === '' && !options.allowEmpty) {
|
||||
throw new Error(
|
||||
'lastFrameRaw() returned an empty string. If this is intentional, use lastFrameRaw({ allowEmpty: true }). ' +
|
||||
'Otherwise, ensure you are calling await waitUntilReady() and that the component is rendering correctly.',
|
||||
);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
};
|
||||
|
||||
lastFrame = (options: { allowEmpty?: boolean } = {}) => {
|
||||
const buffer = this.state.terminal.buffer.active;
|
||||
const allLines: string[] = [];
|
||||
@@ -163,9 +192,7 @@ class XtermStdout extends EventEmitter {
|
||||
}
|
||||
const result = trimmed.join('\n');
|
||||
|
||||
// Normalize for cross-platform snapshot stability:
|
||||
// Normalize any \r\n to \n
|
||||
const normalized = result.replace(/\r\n/g, '\n');
|
||||
const normalized = this.normalizeFrame(result);
|
||||
|
||||
if (normalized === '' && !options.allowEmpty) {
|
||||
throw new Error(
|
||||
@@ -213,9 +240,11 @@ class XtermStdout extends EventEmitter {
|
||||
const currentFrame = stripAnsi(
|
||||
this.lastFrame({ allowEmpty: true }),
|
||||
).trim();
|
||||
const expectedFrame = stripAnsi(this.lastRenderOutput ?? '')
|
||||
.trim()
|
||||
.replace(/\r\n/g, '\n');
|
||||
const expectedFrame = this.normalizeFrame(
|
||||
stripAnsi(
|
||||
(this.lastRenderStaticContent ?? '') + (this.lastRenderOutput ?? ''),
|
||||
),
|
||||
).trim();
|
||||
|
||||
lastCurrent = currentFrame;
|
||||
lastExpected = expectedFrame;
|
||||
@@ -340,6 +369,8 @@ export type RenderInstance = {
|
||||
stdin: XtermStdin;
|
||||
frames: string[];
|
||||
lastFrame: (options?: { allowEmpty?: boolean }) => string;
|
||||
lastFrameRaw: (options?: { allowEmpty?: boolean }) => string;
|
||||
generateSvg: () => string;
|
||||
terminal: Terminal;
|
||||
waitUntilReady: () => Promise<void>;
|
||||
capturedOverflowState: OverflowState | undefined;
|
||||
@@ -424,6 +455,8 @@ export const render = (
|
||||
stdin,
|
||||
frames: stdout.frames,
|
||||
lastFrame: stdout.lastFrame,
|
||||
lastFrameRaw: stdout.lastFrameRaw,
|
||||
generateSvg: stdout.generateSvg,
|
||||
terminal: state.terminal,
|
||||
waitUntilReady: () => stdout.waitUntilReady(),
|
||||
};
|
||||
@@ -574,6 +607,7 @@ const mockUIActions: UIActions = {
|
||||
onHintSubmit: vi.fn(),
|
||||
handleRestart: vi.fn(),
|
||||
handleNewAgentsSelect: vi.fn(),
|
||||
getPreferredEditor: vi.fn(),
|
||||
};
|
||||
|
||||
let capturedOverflowState: OverflowState | undefined;
|
||||
@@ -767,6 +801,7 @@ export function renderHook<Result, Props>(
|
||||
rerender: (props?: Props) => void;
|
||||
unmount: () => void;
|
||||
waitUntilReady: () => Promise<void>;
|
||||
generateSvg: () => string;
|
||||
} {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const result = { current: undefined as unknown as Result };
|
||||
@@ -789,6 +824,7 @@ export function renderHook<Result, Props>(
|
||||
let inkRerender: (tree: React.ReactElement) => void = () => {};
|
||||
let unmount: () => void = () => {};
|
||||
let waitUntilReady: () => Promise<void> = async () => {};
|
||||
let generateSvg: () => string = () => '';
|
||||
|
||||
act(() => {
|
||||
const renderResult = render(
|
||||
@@ -799,6 +835,7 @@ export function renderHook<Result, Props>(
|
||||
inkRerender = renderResult.rerender;
|
||||
unmount = renderResult.unmount;
|
||||
waitUntilReady = renderResult.waitUntilReady;
|
||||
generateSvg = renderResult.generateSvg;
|
||||
});
|
||||
|
||||
function rerender(props?: Props) {
|
||||
@@ -815,7 +852,7 @@ export function renderHook<Result, Props>(
|
||||
});
|
||||
}
|
||||
|
||||
return { result, rerender, unmount, waitUntilReady };
|
||||
return { result, rerender, unmount, waitUntilReady, generateSvg };
|
||||
}
|
||||
|
||||
export function renderHookWithProviders<Result, Props>(
|
||||
@@ -837,6 +874,7 @@ export function renderHookWithProviders<Result, Props>(
|
||||
rerender: (props?: Props) => void;
|
||||
unmount: () => void;
|
||||
waitUntilReady: () => Promise<void>;
|
||||
generateSvg: () => string;
|
||||
} {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const result = { current: undefined as unknown as Result };
|
||||
@@ -887,5 +925,6 @@ export function renderHookWithProviders<Result, Props>(
|
||||
});
|
||||
},
|
||||
waitUntilReady: () => renderResult.waitUntilReady(),
|
||||
generateSvg: () => renderResult.generateSvg(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { Terminal } from '@xterm/headless';
|
||||
|
||||
export const generateSvgForTerminal = (terminal: Terminal): string => {
|
||||
const activeBuffer = terminal.buffer.active;
|
||||
|
||||
const getHexColor = (
|
||||
isRGB: boolean,
|
||||
isPalette: boolean,
|
||||
isDefault: boolean,
|
||||
colorCode: number,
|
||||
): string | null => {
|
||||
if (isDefault) return null;
|
||||
if (isRGB) {
|
||||
return `#${colorCode.toString(16).padStart(6, '0')}`;
|
||||
}
|
||||
if (isPalette) {
|
||||
if (colorCode >= 0 && colorCode <= 15) {
|
||||
return (
|
||||
[
|
||||
'#000000',
|
||||
'#cd0000',
|
||||
'#00cd00',
|
||||
'#cdcd00',
|
||||
'#0000ee',
|
||||
'#cd00cd',
|
||||
'#00cdcd',
|
||||
'#e5e5e5',
|
||||
'#7f7f7f',
|
||||
'#ff0000',
|
||||
'#00ff00',
|
||||
'#ffff00',
|
||||
'#5c5cff',
|
||||
'#ff00ff',
|
||||
'#00ffff',
|
||||
'#ffffff',
|
||||
][colorCode] || null
|
||||
);
|
||||
} else if (colorCode >= 16 && colorCode <= 231) {
|
||||
const v = [0, 95, 135, 175, 215, 255];
|
||||
const c = colorCode - 16;
|
||||
const b = v[c % 6];
|
||||
const g = v[Math.floor(c / 6) % 6];
|
||||
const r = v[Math.floor(c / 36) % 6];
|
||||
return `#${[r, g, b].map((x) => x?.toString(16).padStart(2, '0')).join('')}`;
|
||||
} else if (colorCode >= 232 && colorCode <= 255) {
|
||||
const gray = 8 + (colorCode - 232) * 10;
|
||||
const hex = gray.toString(16).padStart(2, '0');
|
||||
return `#${hex}${hex}${hex}`;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const escapeXml = (unsafe: string): string =>
|
||||
// eslint-disable-next-line no-control-regex
|
||||
unsafe.replace(/[<>&'"\x00-\x08\x0B-\x0C\x0E-\x1F]/g, (c) => {
|
||||
switch (c) {
|
||||
case '<':
|
||||
return '<';
|
||||
case '>':
|
||||
return '>';
|
||||
case '&':
|
||||
return '&';
|
||||
case "'":
|
||||
return ''';
|
||||
case '"':
|
||||
return '"';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
const charWidth = 9;
|
||||
const charHeight = 17;
|
||||
const padding = 10;
|
||||
|
||||
// Find the actual number of rows with content to avoid rendering trailing blank space.
|
||||
let contentRows = terminal.rows;
|
||||
for (let y = terminal.rows - 1; y >= 0; y--) {
|
||||
const line = activeBuffer.getLine(y);
|
||||
if (line && line.translateToString(true).trim().length > 0) {
|
||||
contentRows = y + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (contentRows === 0) contentRows = 1; // Minimum 1 row
|
||||
|
||||
const width = terminal.cols * charWidth + padding * 2;
|
||||
const height = contentRows * charHeight + padding * 2;
|
||||
|
||||
let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
||||
`;
|
||||
svg += ` <style>
|
||||
`;
|
||||
svg += ` text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
`;
|
||||
svg += ` </style>
|
||||
`;
|
||||
svg += ` <rect width="${width}" height="${height}" fill="#000000" />
|
||||
`; // Terminal background
|
||||
svg += ` <g transform="translate(${padding}, ${padding})">
|
||||
`;
|
||||
|
||||
for (let y = 0; y < contentRows; y++) {
|
||||
const line = activeBuffer.getLine(y);
|
||||
if (!line) continue;
|
||||
|
||||
let currentFgHex: string | null = null;
|
||||
let currentBgHex: string | null = null;
|
||||
let currentBlockStartCol = -1;
|
||||
let currentBlockText = '';
|
||||
let currentBlockNumCells = 0;
|
||||
|
||||
const finalizeBlock = (_endCol: number) => {
|
||||
if (currentBlockStartCol !== -1) {
|
||||
if (currentBlockText.length > 0) {
|
||||
const xPos = currentBlockStartCol * charWidth;
|
||||
const yPos = y * charHeight;
|
||||
|
||||
if (currentBgHex) {
|
||||
const rectWidth = currentBlockNumCells * charWidth;
|
||||
svg += ` <rect x="${xPos}" y="${yPos}" width="${rectWidth}" height="${charHeight}" fill="${currentBgHex}" />
|
||||
`;
|
||||
}
|
||||
if (currentBlockText.trim().length > 0) {
|
||||
const fill = currentFgHex || '#ffffff'; // Default text color
|
||||
const textWidth = currentBlockNumCells * charWidth;
|
||||
// Use textLength to ensure the block fits exactly into its designated cells
|
||||
svg += ` <text x="${xPos}" y="${yPos + 2}" fill="${fill}" textLength="${textWidth}" lengthAdjust="spacingAndGlyphs">${escapeXml(currentBlockText)}</text>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (let x = 0; x < line.length; x++) {
|
||||
const cell = line.getCell(x);
|
||||
if (!cell) continue;
|
||||
const cellWidth = cell.getWidth();
|
||||
if (cellWidth === 0) continue; // Skip continuation cells of wide characters
|
||||
|
||||
let fgHex = getHexColor(
|
||||
cell.isFgRGB(),
|
||||
cell.isFgPalette(),
|
||||
cell.isFgDefault(),
|
||||
cell.getFgColor(),
|
||||
);
|
||||
let bgHex = getHexColor(
|
||||
cell.isBgRGB(),
|
||||
cell.isBgPalette(),
|
||||
cell.isBgDefault(),
|
||||
cell.getBgColor(),
|
||||
);
|
||||
|
||||
if (cell.isInverse()) {
|
||||
const tempFgHex = fgHex;
|
||||
fgHex = bgHex || '#000000';
|
||||
bgHex = tempFgHex || '#ffffff';
|
||||
}
|
||||
|
||||
let chars = cell.getChars();
|
||||
if (chars === '') chars = ' '.repeat(cellWidth);
|
||||
|
||||
if (
|
||||
fgHex !== currentFgHex ||
|
||||
bgHex !== currentBgHex ||
|
||||
currentBlockStartCol === -1
|
||||
) {
|
||||
finalizeBlock(x);
|
||||
currentFgHex = fgHex;
|
||||
currentBgHex = bgHex;
|
||||
currentBlockStartCol = x;
|
||||
currentBlockText = chars;
|
||||
currentBlockNumCells = cellWidth;
|
||||
} else {
|
||||
currentBlockText += chars;
|
||||
currentBlockNumCells += cellWidth;
|
||||
}
|
||||
}
|
||||
finalizeBlock(line.length);
|
||||
}
|
||||
svg += ` </g>\n</svg>`;
|
||||
return svg;
|
||||
};
|
||||
@@ -2554,6 +2554,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
}
|
||||
setNewAgents(null);
|
||||
},
|
||||
getPreferredEditor,
|
||||
}),
|
||||
[
|
||||
handleThemeSelect,
|
||||
@@ -2605,6 +2606,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
newAgents,
|
||||
config,
|
||||
historyManager,
|
||||
getPreferredEditor,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
exports[`App > Snapshots > renders default layout correctly 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -47,14 +47,14 @@ exports[`App > Snapshots > renders screen reader layout correctly 1`] = `
|
||||
"Notifications
|
||||
Footer
|
||||
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -67,14 +67,14 @@ Composer
|
||||
|
||||
exports[`App > Snapshots > renders with dialogs visible 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
|
||||
|
||||
@@ -110,14 +110,14 @@ DialogManager
|
||||
|
||||
exports[`App > should render ToolConfirmationQueue along with Composer when tool is confirming and experiment is on 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
|
||||
@@ -25,6 +25,7 @@ import { debugLogger } from '@google/gemini-cli-core';
|
||||
export const GITHUB_WORKFLOW_PATHS = [
|
||||
'gemini-dispatch/gemini-dispatch.yml',
|
||||
'gemini-assistant/gemini-invoke.yml',
|
||||
'gemini-assistant/gemini-plan-execute.yml',
|
||||
'issue-triage/gemini-triage.yml',
|
||||
'issue-triage/gemini-scheduled-triage.yml',
|
||||
'pr-review/gemini-review.yml',
|
||||
@@ -32,6 +33,7 @@ export const GITHUB_WORKFLOW_PATHS = [
|
||||
|
||||
export const GITHUB_COMMANDS_PATHS = [
|
||||
'gemini-assistant/gemini-invoke.toml',
|
||||
'gemini-assistant/gemini-plan-execute.toml',
|
||||
'issue-triage/gemini-scheduled-triage.toml',
|
||||
'issue-triage/gemini-triage.toml',
|
||||
'pr-review/gemini-review.toml',
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('<AnsiOutputText />', () => {
|
||||
<AnsiOutputText data={data} width={80} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
expect(lastFrame()).toBe('Hello, world!\n');
|
||||
expect(lastFrame().trim()).toBe('Hello, world!');
|
||||
unmount();
|
||||
});
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('<AnsiOutputText />', () => {
|
||||
<AnsiOutputText data={data} width={80} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
expect(lastFrame()).toBe(text + '\n');
|
||||
expect(lastFrame().trim()).toBe(text);
|
||||
unmount();
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('<AnsiOutputText />', () => {
|
||||
<AnsiOutputText data={data} width={80} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
expect(lastFrame()).toBe(text + '\n');
|
||||
expect(lastFrame().trim()).toBe(text);
|
||||
unmount();
|
||||
});
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import { renderWithProviders } from '../../test-utils/render.js';
|
||||
import { waitFor } from '../../test-utils/async.js';
|
||||
import { AskUserDialog } from './AskUserDialog.js';
|
||||
import { QuestionType, type Question } from '@google/gemini-cli-core';
|
||||
import chalk from 'chalk';
|
||||
import { UIStateContext, type UIState } from '../contexts/UIStateContext.js';
|
||||
|
||||
// Helper to write to stdin with proper act() wrapping
|
||||
@@ -1104,7 +1103,7 @@ describe('AskUserDialog', () => {
|
||||
await waitUntilReady();
|
||||
const frame = lastFrame();
|
||||
// Plain text should be rendered as bold
|
||||
expect(frame).toContain(chalk.bold('Which option do you prefer?'));
|
||||
expect(frame).toContain('Which option do you prefer?');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1136,7 +1135,7 @@ describe('AskUserDialog', () => {
|
||||
// Should NOT have double-bold (the whole question bolded AND "this" bolded)
|
||||
// "Is " should not be bold, only "this" should be bold
|
||||
expect(frame).toContain('Is ');
|
||||
expect(frame).toContain(chalk.bold('this'));
|
||||
expect(frame).toContain('this');
|
||||
expect(frame).not.toContain('**this**');
|
||||
});
|
||||
});
|
||||
@@ -1166,8 +1165,8 @@ describe('AskUserDialog', () => {
|
||||
await waitFor(async () => {
|
||||
await waitUntilReady();
|
||||
const frame = lastFrame();
|
||||
// Check for chalk.bold('this') - asterisks should be gone, text should be bold
|
||||
expect(frame).toContain(chalk.bold('this'));
|
||||
// Check for 'this' - asterisks should be gone
|
||||
expect(frame).toContain('this');
|
||||
expect(frame).not.toContain('**this**');
|
||||
});
|
||||
});
|
||||
@@ -1198,8 +1197,8 @@ describe('AskUserDialog', () => {
|
||||
await waitUntilReady();
|
||||
const frame = lastFrame();
|
||||
// Backticks should be removed
|
||||
expect(frame).toContain('npm start');
|
||||
expect(frame).not.toContain('`npm start`');
|
||||
expect(frame).toContain('Run npm start?');
|
||||
expect(frame).not.toContain('`');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -183,6 +183,10 @@ interface AskUserDialogProps {
|
||||
* Height constraint for scrollable content.
|
||||
*/
|
||||
availableHeight?: number;
|
||||
/**
|
||||
* Custom keyboard shortcut hints (e.g., ["Ctrl+P to edit"])
|
||||
*/
|
||||
extraParts?: string[];
|
||||
}
|
||||
|
||||
interface ReviewViewProps {
|
||||
@@ -190,6 +194,7 @@ interface ReviewViewProps {
|
||||
answers: { [key: string]: string };
|
||||
onSubmit: () => void;
|
||||
progressHeader?: React.ReactNode;
|
||||
extraParts?: string[];
|
||||
}
|
||||
|
||||
const ReviewView: React.FC<ReviewViewProps> = ({
|
||||
@@ -197,6 +202,7 @@ const ReviewView: React.FC<ReviewViewProps> = ({
|
||||
answers,
|
||||
onSubmit,
|
||||
progressHeader,
|
||||
extraParts,
|
||||
}) => {
|
||||
const unansweredCount = questions.length - Object.keys(answers).length;
|
||||
const hasUnanswered = unansweredCount > 0;
|
||||
@@ -247,6 +253,7 @@ const ReviewView: React.FC<ReviewViewProps> = ({
|
||||
<DialogFooter
|
||||
primaryAction="Enter to submit"
|
||||
navigationActions="Tab/Shift+Tab to edit answers"
|
||||
extraParts={extraParts}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
@@ -925,6 +932,7 @@ export const AskUserDialog: React.FC<AskUserDialogProps> = ({
|
||||
onActiveTextInputChange,
|
||||
width,
|
||||
availableHeight: availableHeightProp,
|
||||
extraParts,
|
||||
}) => {
|
||||
const uiState = useContext(UIStateContext);
|
||||
const availableHeight =
|
||||
@@ -1120,6 +1128,7 @@ export const AskUserDialog: React.FC<AskUserDialogProps> = ({
|
||||
answers={answers}
|
||||
onSubmit={handleReviewSubmit}
|
||||
progressHeader={progressHeader}
|
||||
extraParts={extraParts}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
@@ -1143,6 +1152,7 @@ export const AskUserDialog: React.FC<AskUserDialogProps> = ({
|
||||
? undefined
|
||||
: '↑/↓ to navigate'
|
||||
}
|
||||
extraParts={extraParts}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { act } from 'react';
|
||||
import type { EventEmitter } from 'node:events';
|
||||
import { render } from '../../test-utils/render.js';
|
||||
import { renderWithProviders } from '../../test-utils/render.js';
|
||||
import { waitFor } from '../../test-utils/async.js';
|
||||
import { ConfigInitDisplay } from './ConfigInitDisplay.js';
|
||||
import {
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
import { Text } from 'ink';
|
||||
|
||||
// Mock GeminiSpinner
|
||||
vi.mock('./GeminiRespondingSpinner.js', () => ({
|
||||
vi.mock('./GeminiSpinner.js', () => ({
|
||||
GeminiSpinner: () => <Text>Spinner</Text>,
|
||||
}));
|
||||
|
||||
@@ -43,7 +43,9 @@ describe('ConfigInitDisplay', () => {
|
||||
});
|
||||
|
||||
it('renders initial state', async () => {
|
||||
const { lastFrame, waitUntilReady } = render(<ConfigInitDisplay />);
|
||||
const { lastFrame, waitUntilReady } = renderWithProviders(
|
||||
<ConfigInitDisplay />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -57,7 +59,7 @@ describe('ConfigInitDisplay', () => {
|
||||
return coreEvents;
|
||||
});
|
||||
|
||||
const { lastFrame } = render(<ConfigInitDisplay />);
|
||||
const { lastFrame } = renderWithProviders(<ConfigInitDisplay />);
|
||||
|
||||
// Wait for listener to be registered
|
||||
await waitFor(() => {
|
||||
@@ -95,7 +97,7 @@ describe('ConfigInitDisplay', () => {
|
||||
return coreEvents;
|
||||
});
|
||||
|
||||
const { lastFrame } = render(<ConfigInitDisplay />);
|
||||
const { lastFrame } = renderWithProviders(<ConfigInitDisplay />);
|
||||
|
||||
await waitFor(() => {
|
||||
if (!listener) throw new Error('Listener not registered yet');
|
||||
@@ -131,7 +133,7 @@ describe('ConfigInitDisplay', () => {
|
||||
return coreEvents;
|
||||
});
|
||||
|
||||
const { lastFrame } = render(<ConfigInitDisplay />);
|
||||
const { lastFrame } = renderWithProviders(<ConfigInitDisplay />);
|
||||
|
||||
await waitFor(() => {
|
||||
if (!listener) throw new Error('Listener not registered yet');
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
type McpClient,
|
||||
MCPServerStatus,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { GeminiSpinner } from './GeminiRespondingSpinner.js';
|
||||
import { GeminiSpinner } from './GeminiSpinner.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
|
||||
export const ConfigInitDisplay = ({
|
||||
|
||||
@@ -135,6 +135,7 @@ export const DialogManager = ({
|
||||
isModelNotFoundError={
|
||||
!!uiState.quota.proQuotaRequest.isModelNotFoundError
|
||||
}
|
||||
authType={uiState.quota.proQuotaRequest.authType}
|
||||
onChoice={uiActions.handleProQuotaChoice}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { waitFor } from '../../test-utils/async.js';
|
||||
import { ExitPlanModeDialog } from './ExitPlanModeDialog.js';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
import { keyMatchers, Command } from '../keyMatchers.js';
|
||||
import { openFileInEditor } from '../utils/editorUtils.js';
|
||||
import {
|
||||
ApprovalMode,
|
||||
validatePlanContent,
|
||||
@@ -19,6 +20,10 @@ import {
|
||||
} from '@google/gemini-cli-core';
|
||||
import * as fs from 'node:fs';
|
||||
|
||||
vi.mock('../utils/editorUtils.js', () => ({
|
||||
openFileInEditor: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import('@google/gemini-cli-core')>();
|
||||
@@ -144,6 +149,7 @@ Implement a comprehensive authentication system with multiple providers.
|
||||
onApprove={onApprove}
|
||||
onFeedback={onFeedback}
|
||||
onCancel={onCancel}
|
||||
getPreferredEditor={vi.fn()}
|
||||
width={80}
|
||||
availableHeight={24}
|
||||
/>,
|
||||
@@ -153,6 +159,7 @@ Implement a comprehensive authentication system with multiple providers.
|
||||
getTargetDir: () => mockTargetDir,
|
||||
getIdeMode: () => false,
|
||||
isTrustedFolder: () => true,
|
||||
getPreferredEditor: () => undefined,
|
||||
storage: {
|
||||
getPlansDir: () => mockPlansDir,
|
||||
},
|
||||
@@ -418,6 +425,7 @@ Implement a comprehensive authentication system with multiple providers.
|
||||
onApprove={onApprove}
|
||||
onFeedback={onFeedback}
|
||||
onCancel={onCancel}
|
||||
getPreferredEditor={vi.fn()}
|
||||
width={80}
|
||||
availableHeight={24}
|
||||
/>
|
||||
@@ -535,6 +543,40 @@ Implement a comprehensive authentication system with multiple providers.
|
||||
});
|
||||
expect(onFeedback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('opens plan in external editor when Ctrl+X is pressed', async () => {
|
||||
const { stdin, lastFrame } = renderDialog({ useAlternateBuffer });
|
||||
|
||||
await act(async () => {
|
||||
vi.runAllTimers();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('Add user authentication');
|
||||
});
|
||||
|
||||
// Reset the mock to track the second call during refresh
|
||||
vi.mocked(processSingleFileContent).mockClear();
|
||||
|
||||
// Press Ctrl+X
|
||||
await act(async () => {
|
||||
writeKey(stdin, '\x18'); // Ctrl+X
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(openFileInEditor).toHaveBeenCalledWith(
|
||||
mockPlanFullPath,
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
// Verify that content is refreshed (processSingleFileContent called again)
|
||||
await waitFor(() => {
|
||||
expect(processSingleFileContent).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -5,25 +5,32 @@
|
||||
*/
|
||||
|
||||
import type React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { Box, Text, useStdin } from 'ink';
|
||||
import {
|
||||
ApprovalMode,
|
||||
validatePlanPath,
|
||||
validatePlanContent,
|
||||
QuestionType,
|
||||
type Config,
|
||||
type EditorType,
|
||||
processSingleFileContent,
|
||||
debugLogger,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { useConfig } from '../contexts/ConfigContext.js';
|
||||
import { AskUserDialog } from './AskUserDialog.js';
|
||||
import { openFileInEditor } from '../utils/editorUtils.js';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
import { keyMatchers, Command } from '../keyMatchers.js';
|
||||
import { formatCommand } from '../utils/keybindingUtils.js';
|
||||
|
||||
export interface ExitPlanModeDialogProps {
|
||||
planPath: string;
|
||||
onApprove: (approvalMode: ApprovalMode) => void;
|
||||
onFeedback: (feedback: string) => void;
|
||||
onCancel: () => void;
|
||||
getPreferredEditor: () => EditorType | undefined;
|
||||
width: number;
|
||||
availableHeight?: number;
|
||||
}
|
||||
@@ -38,6 +45,7 @@ interface PlanContentState {
|
||||
status: PlanStatus;
|
||||
content?: string;
|
||||
error?: string;
|
||||
refresh: () => void;
|
||||
}
|
||||
|
||||
enum ApprovalOption {
|
||||
@@ -53,10 +61,15 @@ const StatusMessage: React.FC<{
|
||||
}> = ({ children }) => <Box paddingX={1}>{children}</Box>;
|
||||
|
||||
function usePlanContent(planPath: string, config: Config): PlanContentState {
|
||||
const [state, setState] = useState<PlanContentState>({
|
||||
const [version, setVersion] = useState(0);
|
||||
const [state, setState] = useState<Omit<PlanContentState, 'refresh'>>({
|
||||
status: PlanStatus.Loading,
|
||||
});
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
setVersion((v) => v + 1);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
setState({ status: PlanStatus.Loading });
|
||||
@@ -120,9 +133,9 @@ function usePlanContent(planPath: string, config: Config): PlanContentState {
|
||||
return () => {
|
||||
ignore = true;
|
||||
};
|
||||
}, [planPath, config]);
|
||||
}, [planPath, config, version]);
|
||||
|
||||
return state;
|
||||
return { ...state, refresh };
|
||||
}
|
||||
|
||||
export const ExitPlanModeDialog: React.FC<ExitPlanModeDialogProps> = ({
|
||||
@@ -130,13 +143,36 @@ export const ExitPlanModeDialog: React.FC<ExitPlanModeDialogProps> = ({
|
||||
onApprove,
|
||||
onFeedback,
|
||||
onCancel,
|
||||
getPreferredEditor,
|
||||
width,
|
||||
availableHeight,
|
||||
}) => {
|
||||
const config = useConfig();
|
||||
const { stdin, setRawMode } = useStdin();
|
||||
const planState = usePlanContent(planPath, config);
|
||||
const { refresh } = planState;
|
||||
const [showLoading, setShowLoading] = useState(false);
|
||||
|
||||
const handleOpenEditor = useCallback(async () => {
|
||||
try {
|
||||
await openFileInEditor(planPath, stdin, setRawMode, getPreferredEditor());
|
||||
refresh();
|
||||
} catch (err) {
|
||||
debugLogger.error('Failed to open plan in editor:', err);
|
||||
}
|
||||
}, [planPath, stdin, setRawMode, getPreferredEditor, refresh]);
|
||||
|
||||
useKeypress(
|
||||
(key) => {
|
||||
if (keyMatchers[Command.OPEN_EXTERNAL_EDITOR](key)) {
|
||||
void handleOpenEditor();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
{ isActive: true, priority: true },
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (planState.status !== PlanStatus.Loading) {
|
||||
setShowLoading(false);
|
||||
@@ -183,6 +219,8 @@ export const ExitPlanModeDialog: React.FC<ExitPlanModeDialogProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
const editHint = formatCommand(Command.OPEN_EXTERNAL_EDITOR);
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" width={width}>
|
||||
<AskUserDialog
|
||||
@@ -220,6 +258,7 @@ export const ExitPlanModeDialog: React.FC<ExitPlanModeDialogProps> = ({
|
||||
onCancel={onCancel}
|
||||
width={width}
|
||||
availableHeight={availableHeight}
|
||||
extraParts={[`${editHint} to edit plan`]}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { renderWithProviders } from '../../test-utils/render.js';
|
||||
import { waitFor } from '../../test-utils/async.js';
|
||||
import { act } from 'react';
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { FolderTrustDialog } from './FolderTrustDialog.js';
|
||||
import { ExitCodes } from '@google/gemini-cli-core';
|
||||
import * as processUtils from '../../utils/processUtils.js';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { render } from '../../test-utils/render.js';
|
||||
import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
||||
import { useIsScreenReaderEnabled } from 'ink';
|
||||
import { Text, useIsScreenReaderEnabled } from 'ink';
|
||||
import { StreamingState } from '../types.js';
|
||||
import {
|
||||
SCREEN_READER_LOADING,
|
||||
@@ -24,8 +24,10 @@ vi.mock('ink', async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('./CliSpinner.js', () => ({
|
||||
CliSpinner: () => 'Spinner',
|
||||
vi.mock('./GeminiSpinner.js', () => ({
|
||||
GeminiSpinner: ({ altText }: { altText?: string }) => (
|
||||
<Text>GeminiSpinner {altText}</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
describe('GeminiRespondingSpinner', () => {
|
||||
@@ -33,23 +35,17 @@ describe('GeminiRespondingSpinner', () => {
|
||||
const mockUseIsScreenReaderEnabled = vi.mocked(useIsScreenReaderEnabled);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.clearAllMocks();
|
||||
mockUseIsScreenReaderEnabled.mockReturnValue(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('renders spinner when responding', async () => {
|
||||
mockUseStreamingContext.mockReturnValue(StreamingState.Responding);
|
||||
const { lastFrame, waitUntilReady, unmount } = render(
|
||||
<GeminiRespondingSpinner />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
// Spinner output varies, but it shouldn't be empty
|
||||
expect(lastFrame()).not.toBe('');
|
||||
expect(lastFrame()).toContain('GeminiSpinner');
|
||||
unmount();
|
||||
});
|
||||
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
*/
|
||||
|
||||
import type React from 'react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { Text, useIsScreenReaderEnabled } from 'ink';
|
||||
import { CliSpinner } from './CliSpinner.js';
|
||||
import type { SpinnerName } from 'cli-spinners';
|
||||
import { useStreamingContext } from '../contexts/StreamingContext.js';
|
||||
import { StreamingState } from '../types.js';
|
||||
@@ -16,10 +14,7 @@ import {
|
||||
SCREEN_READER_RESPONDING,
|
||||
} from '../textConstants.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { Colors } from '../colors.js';
|
||||
import tinygradient from 'tinygradient';
|
||||
|
||||
const COLOR_CYCLE_DURATION_MS = 4000;
|
||||
import { GeminiSpinner } from './GeminiSpinner.js';
|
||||
|
||||
interface GeminiRespondingSpinnerProps {
|
||||
/**
|
||||
@@ -54,51 +49,3 @@ export const GeminiRespondingSpinner: React.FC<
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
interface GeminiSpinnerProps {
|
||||
spinnerType?: SpinnerName;
|
||||
altText?: string;
|
||||
}
|
||||
|
||||
export const GeminiSpinner: React.FC<GeminiSpinnerProps> = ({
|
||||
spinnerType = 'dots',
|
||||
altText,
|
||||
}) => {
|
||||
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
||||
const [time, setTime] = useState(0);
|
||||
|
||||
const googleGradient = useMemo(() => {
|
||||
const brandColors = [
|
||||
Colors.AccentPurple,
|
||||
Colors.AccentBlue,
|
||||
Colors.AccentCyan,
|
||||
Colors.AccentGreen,
|
||||
Colors.AccentYellow,
|
||||
Colors.AccentRed,
|
||||
];
|
||||
return tinygradient([...brandColors, brandColors[0]]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isScreenReaderEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setTime((prevTime) => prevTime + 30);
|
||||
}, 30); // ~33fps for smooth color transitions
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [isScreenReaderEnabled]);
|
||||
|
||||
const progress = (time % COLOR_CYCLE_DURATION_MS) / COLOR_CYCLE_DURATION_MS;
|
||||
const currentColor = googleGradient.rgbAt(progress).toHexString();
|
||||
|
||||
return isScreenReaderEnabled ? (
|
||||
<Text>{altText}</Text>
|
||||
) : (
|
||||
<Text color={currentColor}>
|
||||
<CliSpinner type={spinnerType} />
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type React from 'react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { Text, useIsScreenReaderEnabled } from 'ink';
|
||||
import { CliSpinner } from './CliSpinner.js';
|
||||
import type { SpinnerName } from 'cli-spinners';
|
||||
import { Colors } from '../colors.js';
|
||||
import tinygradient from 'tinygradient';
|
||||
|
||||
const COLOR_CYCLE_DURATION_MS = 4000;
|
||||
|
||||
interface GeminiSpinnerProps {
|
||||
spinnerType?: SpinnerName;
|
||||
altText?: string;
|
||||
}
|
||||
|
||||
export const GeminiSpinner: React.FC<GeminiSpinnerProps> = ({
|
||||
spinnerType = 'dots',
|
||||
altText,
|
||||
}) => {
|
||||
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
||||
const [time, setTime] = useState(0);
|
||||
|
||||
const googleGradient = useMemo(() => {
|
||||
const brandColors = [
|
||||
Colors.AccentPurple,
|
||||
Colors.AccentBlue,
|
||||
Colors.AccentCyan,
|
||||
Colors.AccentGreen,
|
||||
Colors.AccentYellow,
|
||||
Colors.AccentRed,
|
||||
];
|
||||
return tinygradient([...brandColors, brandColors[0]]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isScreenReaderEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setTime((prevTime) => prevTime + 30);
|
||||
}, 30); // ~33fps for smooth color transitions
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [isScreenReaderEnabled]);
|
||||
|
||||
const progress = (time % COLOR_CYCLE_DURATION_MS) / COLOR_CYCLE_DURATION_MS;
|
||||
const currentColor = googleGradient.rgbAt(progress).toHexString();
|
||||
|
||||
return isScreenReaderEnabled ? (
|
||||
<Text>{altText}</Text>
|
||||
) : (
|
||||
<Text color={currentColor}>
|
||||
<CliSpinner type={spinnerType} />
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
@@ -411,6 +411,73 @@ describe('InputPrompt', () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should submit command in shell mode when Enter pressed with suggestions visible but no arrow navigation', async () => {
|
||||
props.shellModeActive = true;
|
||||
props.buffer.setText('ls ');
|
||||
|
||||
mockedUseCommandCompletion.mockReturnValue({
|
||||
...mockCommandCompletion,
|
||||
showSuggestions: true,
|
||||
suggestions: [
|
||||
{ label: 'dir1', value: 'dir1' },
|
||||
{ label: 'dir2', value: 'dir2' },
|
||||
],
|
||||
activeSuggestionIndex: 0,
|
||||
});
|
||||
|
||||
const { stdin, unmount } = renderWithProviders(<InputPrompt {...props} />, {
|
||||
uiActions,
|
||||
});
|
||||
|
||||
// Press Enter without navigating — should dismiss suggestions and fall
|
||||
// through to the main submit handler.
|
||||
await act(async () => {
|
||||
stdin.write('\r');
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockCommandCompletion.resetCompletionState).toHaveBeenCalled();
|
||||
expect(props.onSubmit).toHaveBeenCalledWith('ls'); // Assert fall-through (text is trimmed)
|
||||
});
|
||||
expect(mockCommandCompletion.handleAutocomplete).not.toHaveBeenCalled();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should accept suggestion in shell mode when Enter pressed after arrow navigation', async () => {
|
||||
props.shellModeActive = true;
|
||||
props.buffer.setText('ls ');
|
||||
|
||||
mockedUseCommandCompletion.mockReturnValue({
|
||||
...mockCommandCompletion,
|
||||
showSuggestions: true,
|
||||
suggestions: [
|
||||
{ label: 'dir1', value: 'dir1' },
|
||||
{ label: 'dir2', value: 'dir2' },
|
||||
],
|
||||
activeSuggestionIndex: 1,
|
||||
});
|
||||
|
||||
const { stdin, unmount } = renderWithProviders(<InputPrompt {...props} />, {
|
||||
uiActions,
|
||||
});
|
||||
|
||||
// Press ArrowDown to navigate, then Enter to accept
|
||||
await act(async () => {
|
||||
stdin.write('\u001B[B'); // ArrowDown — sets hasUserNavigatedSuggestions
|
||||
});
|
||||
await waitFor(() =>
|
||||
expect(mockCommandCompletion.navigateDown).toHaveBeenCalled(),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
stdin.write('\r'); // Enter — should accept navigated suggestion
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockCommandCompletion.handleAutocomplete).toHaveBeenCalledWith(1);
|
||||
});
|
||||
expect(props.onSubmit).not.toHaveBeenCalled();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should NOT call shell history methods when not in shell mode', async () => {
|
||||
props.buffer.setText('some text');
|
||||
const { stdin, unmount } = renderWithProviders(<InputPrompt {...props} />, {
|
||||
@@ -1537,7 +1604,7 @@ describe('InputPrompt', () => {
|
||||
const { stdout, unmount } = renderWithProviders(<InputPrompt {...props} />);
|
||||
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
// In plan mode it uses '>' but with success color.
|
||||
// We check that it contains '>' and not '*' or '!'.
|
||||
expect(frame).toContain('>');
|
||||
@@ -1593,7 +1660,7 @@ describe('InputPrompt', () => {
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
expect(frame).toContain('▀');
|
||||
expect(frame).toContain('▄');
|
||||
});
|
||||
@@ -1626,7 +1693,7 @@ describe('InputPrompt', () => {
|
||||
const expectedBgColor = isWhite ? '#eeeeee' : '#1c1c1c';
|
||||
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
|
||||
// Use chalk to get the expected background color escape sequence
|
||||
const bgCheck = chalk.bgHex(expectedBgColor)(' ');
|
||||
@@ -1658,7 +1725,7 @@ describe('InputPrompt', () => {
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
expect(frame).not.toContain('▀');
|
||||
expect(frame).not.toContain('▄');
|
||||
// It SHOULD have horizontal fallback lines
|
||||
@@ -1681,7 +1748,7 @@ describe('InputPrompt', () => {
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
|
||||
expect(frame).toContain('▀');
|
||||
|
||||
@@ -1705,7 +1772,7 @@ describe('InputPrompt', () => {
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
|
||||
// Should NOT have background characters
|
||||
|
||||
@@ -1734,7 +1801,7 @@ describe('InputPrompt', () => {
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
expect(frame).not.toContain('▀');
|
||||
expect(frame).not.toContain('▄');
|
||||
// Check for Box borders (round style uses unicode box chars)
|
||||
@@ -1974,7 +2041,7 @@ describe('InputPrompt', () => {
|
||||
name: 'at the end of a line with unicode characters',
|
||||
text: 'hello 👍',
|
||||
visualCursor: [0, 8],
|
||||
expected: `hello 👍${chalk.inverse(' ')}`,
|
||||
expected: `hello 👍`, // skip checking inverse ansi due to ink truncation bug
|
||||
},
|
||||
{
|
||||
name: 'at the end of a short line with unicode characters',
|
||||
@@ -1996,7 +2063,7 @@ describe('InputPrompt', () => {
|
||||
},
|
||||
])(
|
||||
'should display cursor correctly $name',
|
||||
async ({ text, visualCursor, expected }) => {
|
||||
async ({ name, text, visualCursor, expected }) => {
|
||||
mockBuffer.text = text;
|
||||
mockBuffer.lines = [text];
|
||||
mockBuffer.viewportVisualLines = [text];
|
||||
@@ -2007,8 +2074,14 @@ describe('InputPrompt', () => {
|
||||
<InputPrompt {...props} />,
|
||||
);
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
expect(frame).toContain(expected);
|
||||
const frame = stdout.lastFrameRaw();
|
||||
expect(stripAnsi(frame)).toContain(stripAnsi(expected));
|
||||
if (
|
||||
name !== 'at the end of a line with unicode characters' &&
|
||||
name !== 'on a highlighted token'
|
||||
) {
|
||||
expect(frame).toContain('\u001b[7m');
|
||||
}
|
||||
});
|
||||
unmount();
|
||||
},
|
||||
@@ -2050,7 +2123,7 @@ describe('InputPrompt', () => {
|
||||
},
|
||||
])(
|
||||
'should display cursor correctly $name in a multiline block',
|
||||
async ({ text, visualCursor, expected, visualToLogicalMap }) => {
|
||||
async ({ name, text, visualCursor, expected, visualToLogicalMap }) => {
|
||||
mockBuffer.text = text;
|
||||
mockBuffer.lines = text.split('\n');
|
||||
mockBuffer.viewportVisualLines = text.split('\n');
|
||||
@@ -2064,8 +2137,14 @@ describe('InputPrompt', () => {
|
||||
<InputPrompt {...props} />,
|
||||
);
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
expect(frame).toContain(expected);
|
||||
const frame = stdout.lastFrameRaw();
|
||||
expect(stripAnsi(frame)).toContain(stripAnsi(expected));
|
||||
if (
|
||||
name !== 'at the end of a line with unicode characters' &&
|
||||
name !== 'on a highlighted token'
|
||||
) {
|
||||
expect(frame).toContain('\u001b[7m');
|
||||
}
|
||||
});
|
||||
unmount();
|
||||
},
|
||||
@@ -2088,7 +2167,7 @@ describe('InputPrompt', () => {
|
||||
<InputPrompt {...props} />,
|
||||
);
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
const lines = frame.split('\n');
|
||||
// The line with the cursor should just be an inverted space inside the box border
|
||||
expect(
|
||||
@@ -2120,7 +2199,7 @@ describe('InputPrompt', () => {
|
||||
<InputPrompt {...props} />,
|
||||
);
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
// Check that all lines, including the empty one, are rendered.
|
||||
// This implicitly tests that the Box wrapper provides height for the empty line.
|
||||
expect(frame).toContain('hello');
|
||||
@@ -2655,7 +2734,7 @@ describe('InputPrompt', () => {
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame();
|
||||
const frame = stdout.lastFrameRaw();
|
||||
expect(frame).toContain('(r:)');
|
||||
expect(frame).toContain('echo hello');
|
||||
expect(frame).toContain('echo world');
|
||||
@@ -2926,7 +3005,7 @@ describe('InputPrompt', () => {
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const frame = stdout.lastFrame() ?? '';
|
||||
const frame = stdout.lastFrameRaw() ?? '';
|
||||
expect(frame).toContain('(r:)');
|
||||
expect(frame).toContain('git commit');
|
||||
expect(frame).toContain('git push');
|
||||
|
||||
@@ -254,6 +254,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
>(null);
|
||||
const pasteTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const innerBoxRef = useRef<DOMElement>(null);
|
||||
const hasUserNavigatedSuggestions = useRef(false);
|
||||
|
||||
const [reverseSearchActive, setReverseSearchActive] = useState(false);
|
||||
const [commandSearchActive, setCommandSearchActive] = useState(false);
|
||||
@@ -610,6 +611,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
setSuppressCompletion(
|
||||
isHistoryNav || isCursorMovement || keyMatchers[Command.ESCAPE](key),
|
||||
);
|
||||
hasUserNavigatedSuggestions.current = false;
|
||||
}
|
||||
|
||||
// TODO(jacobr): this special case is likely not needed anymore.
|
||||
@@ -643,7 +645,13 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
Boolean(completion.promptCompletion.text) ||
|
||||
reverseSearchActive ||
|
||||
commandSearchActive;
|
||||
if (isPlainTab) {
|
||||
|
||||
if (isPlainTab && shellModeActive) {
|
||||
resetPlainTabPress();
|
||||
if (!completion.showSuggestions) {
|
||||
setSuppressCompletion(false);
|
||||
}
|
||||
} else if (isPlainTab) {
|
||||
if (!hasTabCompletionInteraction) {
|
||||
if (registerPlainTabPress() === 2) {
|
||||
toggleCleanUiDetailsVisible();
|
||||
@@ -903,11 +911,13 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
if (completion.suggestions.length > 1) {
|
||||
if (keyMatchers[Command.COMPLETION_UP](key)) {
|
||||
completion.navigateUp();
|
||||
hasUserNavigatedSuggestions.current = true;
|
||||
setExpandedSuggestionIndex(-1); // Reset expansion when navigating
|
||||
return true;
|
||||
}
|
||||
if (keyMatchers[Command.COMPLETION_DOWN](key)) {
|
||||
completion.navigateDown();
|
||||
hasUserNavigatedSuggestions.current = true;
|
||||
setExpandedSuggestionIndex(-1); // Reset expansion when navigating
|
||||
return true;
|
||||
}
|
||||
@@ -925,6 +935,24 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
|
||||
const isEnterKey = key.name === 'return' && !key.ctrl;
|
||||
|
||||
if (isEnterKey && shellModeActive) {
|
||||
if (hasUserNavigatedSuggestions.current) {
|
||||
completion.handleAutocomplete(
|
||||
completion.activeSuggestionIndex,
|
||||
);
|
||||
setExpandedSuggestionIndex(-1);
|
||||
hasUserNavigatedSuggestions.current = false;
|
||||
return true;
|
||||
}
|
||||
completion.resetCompletionState();
|
||||
setExpandedSuggestionIndex(-1);
|
||||
hasUserNavigatedSuggestions.current = false;
|
||||
if (buffer.text.trim()) {
|
||||
handleSubmit(buffer.text);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isEnterKey && buffer.text.startsWith('/')) {
|
||||
const { isArgumentCompletion, leafCommand } =
|
||||
completion.slashCompletionRange;
|
||||
@@ -1381,7 +1409,8 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
scrollOffset={activeCompletion.visibleStartIndex}
|
||||
userInput={buffer.text}
|
||||
mode={
|
||||
completion.completionMode === CompletionMode.AT
|
||||
completion.completionMode === CompletionMode.AT ||
|
||||
completion.completionMode === CompletionMode.SHELL
|
||||
? 'reverse'
|
||||
: buffer.text.startsWith('/') &&
|
||||
!reverseSearchActive &&
|
||||
|
||||
@@ -13,6 +13,7 @@ import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||
import {
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
AuthType,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
// Mock the child component to make it easier to test the parent
|
||||
@@ -62,7 +63,7 @@ describe('ProQuotaDialog', () => {
|
||||
|
||||
describe('for non-flash model failures', () => {
|
||||
describe('when it is a terminal quota error', () => {
|
||||
it('should render switch, upgrade, and stop options for paid tiers', () => {
|
||||
it('should render switch, upgrade, and stop options for LOGIN_WITH_GOOGLE', () => {
|
||||
const { unmount } = render(
|
||||
<ProQuotaDialog
|
||||
failedModel="gemini-2.5-pro"
|
||||
@@ -70,6 +71,7 @@ describe('ProQuotaDialog', () => {
|
||||
message="paid tier quota error"
|
||||
isTerminalQuotaError={true}
|
||||
isModelNotFoundError={false}
|
||||
authType={AuthType.LOGIN_WITH_GOOGLE}
|
||||
onChoice={mockOnChoice}
|
||||
/>,
|
||||
);
|
||||
@@ -99,6 +101,39 @@ describe('ProQuotaDialog', () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should NOT render upgrade option for USE_GEMINI', () => {
|
||||
const { unmount } = render(
|
||||
<ProQuotaDialog
|
||||
failedModel="gemini-2.5-pro"
|
||||
fallbackModel="gemini-2.5-flash"
|
||||
message="paid tier quota error"
|
||||
isTerminalQuotaError={true}
|
||||
isModelNotFoundError={false}
|
||||
authType={AuthType.USE_GEMINI}
|
||||
onChoice={mockOnChoice}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(RadioButtonSelect).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
items: [
|
||||
{
|
||||
label: 'Switch to gemini-2.5-flash',
|
||||
value: 'retry_always',
|
||||
key: 'retry_always',
|
||||
},
|
||||
{
|
||||
label: 'Stop',
|
||||
value: 'retry_later',
|
||||
key: 'retry_later',
|
||||
},
|
||||
],
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should render "Keep trying" and "Stop" options when failed model and fallback model are the same', () => {
|
||||
const { unmount } = render(
|
||||
<ProQuotaDialog
|
||||
@@ -130,7 +165,7 @@ describe('ProQuotaDialog', () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should render switch, upgrade, and stop options for free tier', () => {
|
||||
it('should render switch, upgrade, and stop options for LOGIN_WITH_GOOGLE (free tier)', () => {
|
||||
const { unmount } = render(
|
||||
<ProQuotaDialog
|
||||
failedModel="gemini-2.5-pro"
|
||||
@@ -138,6 +173,7 @@ describe('ProQuotaDialog', () => {
|
||||
message="free tier quota error"
|
||||
isTerminalQuotaError={true}
|
||||
isModelNotFoundError={false}
|
||||
authType={AuthType.LOGIN_WITH_GOOGLE}
|
||||
onChoice={mockOnChoice}
|
||||
/>,
|
||||
);
|
||||
@@ -204,7 +240,7 @@ describe('ProQuotaDialog', () => {
|
||||
});
|
||||
|
||||
describe('when it is a model not found error', () => {
|
||||
it('should render switch and stop options regardless of tier', () => {
|
||||
it('should render switch, upgrade, and stop options for LOGIN_WITH_GOOGLE', () => {
|
||||
const { unmount } = render(
|
||||
<ProQuotaDialog
|
||||
failedModel="gemini-3-pro-preview"
|
||||
@@ -212,6 +248,7 @@ describe('ProQuotaDialog', () => {
|
||||
message="You don't have access to gemini-3-pro-preview yet."
|
||||
isTerminalQuotaError={false}
|
||||
isModelNotFoundError={true}
|
||||
authType={AuthType.LOGIN_WITH_GOOGLE}
|
||||
onChoice={mockOnChoice}
|
||||
/>,
|
||||
);
|
||||
@@ -241,7 +278,7 @@ describe('ProQuotaDialog', () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should render switch and stop options for paid tier as well', () => {
|
||||
it('should NOT render upgrade option for USE_GEMINI', () => {
|
||||
const { unmount } = render(
|
||||
<ProQuotaDialog
|
||||
failedModel="gemini-3-pro-preview"
|
||||
@@ -249,6 +286,7 @@ describe('ProQuotaDialog', () => {
|
||||
message="You don't have access to gemini-3-pro-preview yet."
|
||||
isTerminalQuotaError={false}
|
||||
isModelNotFoundError={true}
|
||||
authType={AuthType.USE_GEMINI}
|
||||
onChoice={mockOnChoice}
|
||||
/>,
|
||||
);
|
||||
@@ -261,11 +299,6 @@ describe('ProQuotaDialog', () => {
|
||||
value: 'retry_always',
|
||||
key: 'retry_always',
|
||||
},
|
||||
{
|
||||
label: 'Upgrade for higher limits',
|
||||
value: 'upgrade',
|
||||
key: 'upgrade',
|
||||
},
|
||||
{
|
||||
label: 'Stop',
|
||||
value: 'retry_later',
|
||||
|
||||
@@ -8,6 +8,7 @@ import type React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { AuthType } from '@google/gemini-cli-core';
|
||||
|
||||
interface ProQuotaDialogProps {
|
||||
failedModel: string;
|
||||
@@ -15,6 +16,7 @@ interface ProQuotaDialogProps {
|
||||
message: string;
|
||||
isTerminalQuotaError: boolean;
|
||||
isModelNotFoundError?: boolean;
|
||||
authType?: AuthType;
|
||||
onChoice: (
|
||||
choice: 'retry_later' | 'retry_once' | 'retry_always' | 'upgrade',
|
||||
) => void;
|
||||
@@ -26,6 +28,7 @@ export function ProQuotaDialog({
|
||||
message,
|
||||
isTerminalQuotaError,
|
||||
isModelNotFoundError,
|
||||
authType,
|
||||
onChoice,
|
||||
}: ProQuotaDialogProps): React.JSX.Element {
|
||||
let items;
|
||||
@@ -51,11 +54,15 @@ export function ProQuotaDialog({
|
||||
value: 'retry_always' as const,
|
||||
key: 'retry_always',
|
||||
},
|
||||
{
|
||||
label: 'Upgrade for higher limits',
|
||||
value: 'upgrade' as const,
|
||||
key: 'upgrade',
|
||||
},
|
||||
...(authType === AuthType.LOGIN_WITH_GOOGLE
|
||||
? [
|
||||
{
|
||||
label: 'Upgrade for higher limits',
|
||||
value: 'upgrade' as const,
|
||||
key: 'upgrade',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: `Stop`,
|
||||
value: 'retry_later' as const,
|
||||
|
||||
@@ -263,16 +263,11 @@ describe('SettingsDialog', () => {
|
||||
const settings = createMockSettings();
|
||||
const onSelect = vi.fn();
|
||||
|
||||
const { lastFrame, waitUntilReady, unmount } = renderDialog(
|
||||
settings,
|
||||
onSelect,
|
||||
);
|
||||
await waitUntilReady();
|
||||
const renderResult = renderDialog(settings, onSelect);
|
||||
await renderResult.waitUntilReady();
|
||||
|
||||
const output = lastFrame();
|
||||
// Use snapshot to capture visual layout including indicators
|
||||
expect(output).toMatchSnapshot();
|
||||
unmount();
|
||||
await expect(renderResult).toMatchSvgSnapshot();
|
||||
renderResult.unmount();
|
||||
});
|
||||
|
||||
it('should use almost full height of the window but no more when the window height is 25 rows', async () => {
|
||||
@@ -1830,18 +1825,15 @@ describe('SettingsDialog', () => {
|
||||
});
|
||||
const onSelect = vi.fn();
|
||||
|
||||
const { lastFrame, stdin, waitUntilReady, unmount } = renderDialog(
|
||||
settings,
|
||||
onSelect,
|
||||
);
|
||||
await waitUntilReady();
|
||||
const renderResult = renderDialog(settings, onSelect);
|
||||
await renderResult.waitUntilReady();
|
||||
|
||||
if (stdinActions) {
|
||||
await stdinActions(stdin, waitUntilReady);
|
||||
await stdinActions(renderResult.stdin, renderResult.waitUntilReady);
|
||||
}
|
||||
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
unmount();
|
||||
await expect(renderResult).toMatchSvgSnapshot();
|
||||
renderResult.unmount();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -19,10 +19,8 @@ describe('Table', () => {
|
||||
{ id: 2, name: 'Bob' },
|
||||
];
|
||||
|
||||
const { lastFrame, waitUntilReady } = render(
|
||||
<Table columns={columns} data={data} />,
|
||||
100,
|
||||
);
|
||||
const renderResult = render(<Table columns={columns} data={data} />, 100);
|
||||
const { lastFrame, waitUntilReady } = renderResult;
|
||||
await waitUntilReady?.();
|
||||
const output = lastFrame();
|
||||
|
||||
@@ -32,7 +30,7 @@ describe('Table', () => {
|
||||
expect(output).toContain('Alice');
|
||||
expect(output).toContain('2');
|
||||
expect(output).toContain('Bob');
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
await expect(renderResult).toMatchSvgSnapshot();
|
||||
});
|
||||
|
||||
it('should support custom cell rendering', async () => {
|
||||
@@ -48,15 +46,13 @@ describe('Table', () => {
|
||||
];
|
||||
const data = [{ value: 10 }];
|
||||
|
||||
const { lastFrame, waitUntilReady } = render(
|
||||
<Table columns={columns} data={data} />,
|
||||
100,
|
||||
);
|
||||
const renderResult = render(<Table columns={columns} data={data} />, 100);
|
||||
const { lastFrame, waitUntilReady } = renderResult;
|
||||
await waitUntilReady?.();
|
||||
const output = lastFrame();
|
||||
|
||||
expect(output).toContain('20');
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
await expect(renderResult).toMatchSvgSnapshot();
|
||||
});
|
||||
|
||||
it('should handle undefined values gracefully', async () => {
|
||||
@@ -70,4 +66,26 @@ describe('Table', () => {
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('undefined');
|
||||
});
|
||||
|
||||
it('should support inverse text rendering', async () => {
|
||||
const columns = [
|
||||
{
|
||||
key: 'status',
|
||||
header: 'Status',
|
||||
flexGrow: 1,
|
||||
renderCell: (item: { status: string }) => (
|
||||
<Text inverse>{item.status}</Text>
|
||||
),
|
||||
},
|
||||
];
|
||||
const data = [{ status: 'Active' }];
|
||||
|
||||
const renderResult = render(<Table columns={columns} data={data} />, 100);
|
||||
const { lastFrame, waitUntilReady } = renderResult;
|
||||
await waitUntilReady?.();
|
||||
const output = lastFrame();
|
||||
|
||||
expect(output).toContain('Active');
|
||||
await expect(renderResult).toMatchSvgSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -255,7 +255,11 @@ describe('ToolConfirmationQueue', () => {
|
||||
total: 1,
|
||||
};
|
||||
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
const {
|
||||
lastFrame,
|
||||
waitUntilReady,
|
||||
unmount = vi.fn(),
|
||||
} = renderWithProviders(
|
||||
<ToolConfirmationQueue
|
||||
confirmingTool={confirmingTool as unknown as ConfirmingToolState}
|
||||
/>,
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ShowMoreLines } from './ShowMoreLines.js';
|
||||
import { StickyHeader } from './StickyHeader.js';
|
||||
import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js';
|
||||
import type { SerializableConfirmationDetails } from '@google/gemini-cli-core';
|
||||
import { useUIActions } from '../contexts/UIActionsContext.js';
|
||||
|
||||
function getConfirmationHeader(
|
||||
details: SerializableConfirmationDetails | undefined,
|
||||
@@ -41,6 +42,7 @@ export const ToolConfirmationQueue: React.FC<ToolConfirmationQueueProps> = ({
|
||||
confirmingTool,
|
||||
}) => {
|
||||
const config = useConfig();
|
||||
const { getPreferredEditor } = useUIActions();
|
||||
const isAlternateBuffer = useAlternateBuffer();
|
||||
const {
|
||||
mainAreaWidth,
|
||||
@@ -134,6 +136,7 @@ export const ToolConfirmationQueue: React.FC<ToolConfirmationQueueProps> = ({
|
||||
callId={tool.callId}
|
||||
confirmationDetails={tool.confirmationDetails}
|
||||
config={config}
|
||||
getPreferredEditor={getPreferredEditor}
|
||||
terminalWidth={mainAreaWidth - 4} // Adjust for parent border/padding
|
||||
availableTerminalHeight={availableContentHeight}
|
||||
isFocused={true}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
exports[`AlternateBufferQuittingDisplay > renders with a tool awaiting confirmation > with_confirming_tool 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -25,14 +25,14 @@ Action Required (was prompted):
|
||||
|
||||
exports[`AlternateBufferQuittingDisplay > renders with active and pending tool messages > with_history_and_pending 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -52,14 +52,14 @@ Tips for getting started:
|
||||
|
||||
exports[`AlternateBufferQuittingDisplay > renders with empty history and no pending items > empty 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -71,14 +71,14 @@ Tips for getting started:
|
||||
|
||||
exports[`AlternateBufferQuittingDisplay > renders with history but no pending items > with_history_no_pending 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -98,14 +98,14 @@ Tips for getting started:
|
||||
|
||||
exports[`AlternateBufferQuittingDisplay > renders with pending items but no history > with_pending_no_history 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -117,14 +117,14 @@ Tips for getting started:
|
||||
|
||||
exports[`AlternateBufferQuittingDisplay > renders with user and gemini messages > with_user_gemini_messages 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -132,7 +132,7 @@ Tips for getting started:
|
||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||
4. /help for more information.
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
> Hello Gemini
|
||||
> Hello Gemini
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
✦ Hello User!
|
||||
"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
exports[`<AppHeader /> > should not render the banner when no flags are set 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -21,14 +21,14 @@ Tips for getting started:
|
||||
|
||||
exports[`<AppHeader /> > should not render the default banner if shown count is 5 or more 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
@@ -40,14 +40,14 @@ Tips for getting started:
|
||||
|
||||
exports[`<AppHeader /> > should render the banner with default text 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ This is the default banner │
|
||||
@@ -62,14 +62,14 @@ Tips for getting started:
|
||||
|
||||
exports[`<AppHeader /> > should render the banner with warning text 1`] = `
|
||||
"
|
||||
███ █████████
|
||||
███ █████████
|
||||
░░░███ ███░░░░░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
░░░███ ███ ░░░
|
||||
░░░███░███
|
||||
███░ ░███ █████
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ There are capacity issues │
|
||||
|
||||
@@ -23,7 +23,7 @@ Files to Modify
|
||||
Approves plan but requires confirmation for each tool
|
||||
3. Type your feedback...
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc to cancel
|
||||
Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -50,7 +50,7 @@ Files to Modify
|
||||
Approves plan but requires confirmation for each tool
|
||||
3. Type your feedback...
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc to cancel
|
||||
Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -82,7 +82,7 @@ Implementation Steps
|
||||
Approves plan but requires confirmation for each tool
|
||||
3. Type your feedback...
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc to cancel
|
||||
Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -109,7 +109,7 @@ Files to Modify
|
||||
Approves plan but requires confirmation for each tool
|
||||
3. Type your feedback...
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc to cancel
|
||||
Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -136,7 +136,7 @@ Files to Modify
|
||||
Approves plan but requires confirmation for each tool
|
||||
3. Type your feedback...
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc to cancel
|
||||
Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -163,7 +163,7 @@ Files to Modify
|
||||
Approves plan but requires confirmation for each tool
|
||||
3. Type your feedback...
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc to cancel
|
||||
Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -216,7 +216,7 @@ Testing Strategy
|
||||
Approves plan but requires confirmation for each tool
|
||||
3. Type your feedback...
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc to cancel
|
||||
Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -243,6 +243,6 @@ Files to Modify
|
||||
Approves plan but requires confirmation for each tool
|
||||
3. Type your feedback...
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc to cancel
|
||||
Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
exports[`InputPrompt > History Navigation and Completion Suppression > should not render suggestions during history navigation 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
> second message
|
||||
> second message
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and collapses long suggestion via Right/Left arrows > command-search-render-collapsed-match 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
(r:) Type your message or @path/to/file
|
||||
(r:) Type your message or @path/to/file
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll →
|
||||
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll →
|
||||
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
|
||||
...
|
||||
"
|
||||
@@ -19,9 +19,9 @@ exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and c
|
||||
|
||||
exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and collapses long suggestion via Right/Left arrows > command-search-render-expanded-match 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
(r:) Type your message or @path/to/file
|
||||
(r:) Type your message or @path/to/file
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll ←
|
||||
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll ←
|
||||
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
|
||||
llllllllllllllllllllllllllllllllllllllllllllllllll
|
||||
"
|
||||
@@ -29,7 +29,7 @@ exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and c
|
||||
|
||||
exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match window and expanded view (snapshots) > command-search-render-collapsed-match 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
(r:) commit
|
||||
(r:) commit
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
git commit -m "feat: add search" in src/app
|
||||
"
|
||||
@@ -37,7 +37,7 @@ exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match
|
||||
|
||||
exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match window and expanded view (snapshots) > command-search-render-expanded-match 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
(r:) commit
|
||||
(r:) commit
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
git commit -m "feat: add search" in src/app
|
||||
"
|
||||
@@ -45,63 +45,63 @@ exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match
|
||||
|
||||
exports[`InputPrompt > image path transformation snapshots > should snapshot collapsed image path 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
> [Image ...reenshot2x.png]
|
||||
> [Image ...reenshot2x.png]
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > image path transformation snapshots > should snapshot expanded image path when cursor is on it 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
> @/path/to/screenshots/screenshot2x.png
|
||||
> @/path/to/screenshots/screenshot2x.png
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
> [Pasted Text: 10 lines]
|
||||
> [Pasted Text: 10 lines]
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 2`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
> [Pasted Text: 10 lines]
|
||||
> [Pasted Text: 10 lines]
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 3`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
> [Pasted Text: 10 lines]
|
||||
> [Pasted Text: 10 lines]
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > snapshots > should not show inverted cursor when shell is focused 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
> Type your message or @path/to/file
|
||||
> Type your message or @path/to/file
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > snapshots > should render correctly in shell mode 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
! Type your message or @path/to/file
|
||||
! Type your message or @path/to/file
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > snapshots > should render correctly in yolo mode 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
* Type your message or @path/to/file
|
||||
* Type your message or @path/to/file
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > snapshots > should render correctly when accepting edits 1`] = `
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
> Type your message or @path/to/file
|
||||
> Type your message or @path/to/file
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="751" viewBox="0 0 920 751">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="751" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="36" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> > Settings </text>
|
||||
<text x="891" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="70" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="891" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<rect x="36" y="85" width="9" height="17" fill="#ffffff" />
|
||||
<text x="36" y="87" fill="#000000" textLength="9" lengthAdjust="spacingAndGlyphs">S</text>
|
||||
<text x="45" y="87" fill="#6c7086" textLength="135" lengthAdjust="spacingAndGlyphs">earch to filter</text>
|
||||
<text x="873" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="104" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
<text x="891" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="138" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▲</text>
|
||||
<text x="891" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="155" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="155" fill="#a6e3a1" textLength="72" lengthAdjust="spacingAndGlyphs">Vim Mode</text>
|
||||
<text x="828" y="155" fill="#a6e3a1" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="172" fill="#6c7086" textLength="198" lengthAdjust="spacingAndGlyphs">Enable Vim keybindings</text>
|
||||
<text x="891" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="206" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> Default Approval Mode </text>
|
||||
<text x="810" y="206" fill="#6c7086" textLength="63" lengthAdjust="spacingAndGlyphs">Default</text>
|
||||
<text x="891" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="223" fill="#6c7086" textLength="738" lengthAdjust="spacingAndGlyphs">The default approval mode for tool execution. 'default' prompts for approval, 'au…</text>
|
||||
<text x="891" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="257" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Enable Auto Update </text>
|
||||
<text x="837" y="257" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="274" fill="#6c7086" textLength="225" lengthAdjust="spacingAndGlyphs">Enable automatic updates.</text>
|
||||
<text x="891" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="308" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Enable Notifications </text>
|
||||
<text x="828" y="308" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="325" fill="#6c7086" textLength="756" lengthAdjust="spacingAndGlyphs">Enable run-event notifications for action-required prompts and session completion. …</text>
|
||||
<text x="891" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="359" fill="#ffffff" textLength="783" lengthAdjust="spacingAndGlyphs"> Plan Directory </text>
|
||||
<text x="792" y="359" fill="#6c7086" textLength="81" lengthAdjust="spacingAndGlyphs">undefined</text>
|
||||
<text x="891" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="376" fill="#6c7086" textLength="720" lengthAdjust="spacingAndGlyphs">The directory where planning artifacts are stored. If not specified, defaults t…</text>
|
||||
<text x="891" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="410" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Plan Model Routing </text>
|
||||
<text x="837" y="410" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="427" fill="#6c7086" textLength="765" lengthAdjust="spacingAndGlyphs">Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr…</text>
|
||||
<text x="891" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="461" fill="#ffffff" textLength="846" lengthAdjust="spacingAndGlyphs"> Max Chat Model Attempts </text>
|
||||
<text x="855" y="461" fill="#6c7086" textLength="18" lengthAdjust="spacingAndGlyphs">10</text>
|
||||
<text x="891" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="478" fill="#6c7086" textLength="729" lengthAdjust="spacingAndGlyphs">Maximum number of attempts for requests to the main chat model. Cannot exceed 10.</text>
|
||||
<text x="891" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="512" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Debug Keystroke Logging </text>
|
||||
<text x="828" y="512" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="529" fill="#6c7086" textLength="450" lengthAdjust="spacingAndGlyphs">Enable debug logging of keystrokes to the console.</text>
|
||||
<text x="891" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="563" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▼</text>
|
||||
<text x="891" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="597" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Apply To </text>
|
||||
<text x="891" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="614" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="614" fill="#a6e3a1" textLength="117" lengthAdjust="spacingAndGlyphs">User Settings</text>
|
||||
<text x="891" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="631" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Workspace Settings </text>
|
||||
<text x="891" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="648" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> System Settings </text>
|
||||
<text x="891" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="682" fill="#6c7086" textLength="657" lengthAdjust="spacingAndGlyphs">(Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close)</text>
|
||||
<text x="891" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="716" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,133 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="751" viewBox="0 0 920 751">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="751" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="36" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> > Settings </text>
|
||||
<text x="891" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="70" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="891" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<rect x="36" y="85" width="9" height="17" fill="#ffffff" />
|
||||
<text x="36" y="87" fill="#000000" textLength="9" lengthAdjust="spacingAndGlyphs">S</text>
|
||||
<text x="45" y="87" fill="#6c7086" textLength="135" lengthAdjust="spacingAndGlyphs">earch to filter</text>
|
||||
<text x="873" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="104" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
<text x="891" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="138" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▲</text>
|
||||
<text x="891" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="155" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="155" fill="#a6e3a1" textLength="72" lengthAdjust="spacingAndGlyphs">Vim Mode</text>
|
||||
<text x="828" y="155" fill="#a6e3a1" textLength="45" lengthAdjust="spacingAndGlyphs">true*</text>
|
||||
<text x="891" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="172" fill="#6c7086" textLength="198" lengthAdjust="spacingAndGlyphs">Enable Vim keybindings</text>
|
||||
<text x="891" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="206" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> Default Approval Mode </text>
|
||||
<text x="810" y="206" fill="#6c7086" textLength="63" lengthAdjust="spacingAndGlyphs">Default</text>
|
||||
<text x="891" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="223" fill="#6c7086" textLength="738" lengthAdjust="spacingAndGlyphs">The default approval mode for tool execution. 'default' prompts for approval, 'au…</text>
|
||||
<text x="891" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="257" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Enable Auto Update </text>
|
||||
<text x="837" y="257" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="274" fill="#6c7086" textLength="225" lengthAdjust="spacingAndGlyphs">Enable automatic updates.</text>
|
||||
<text x="891" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="308" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Enable Notifications </text>
|
||||
<text x="828" y="308" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="325" fill="#6c7086" textLength="756" lengthAdjust="spacingAndGlyphs">Enable run-event notifications for action-required prompts and session completion. …</text>
|
||||
<text x="891" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="359" fill="#ffffff" textLength="783" lengthAdjust="spacingAndGlyphs"> Plan Directory </text>
|
||||
<text x="792" y="359" fill="#6c7086" textLength="81" lengthAdjust="spacingAndGlyphs">undefined</text>
|
||||
<text x="891" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="376" fill="#6c7086" textLength="720" lengthAdjust="spacingAndGlyphs">The directory where planning artifacts are stored. If not specified, defaults t…</text>
|
||||
<text x="891" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="410" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Plan Model Routing </text>
|
||||
<text x="837" y="410" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="427" fill="#6c7086" textLength="765" lengthAdjust="spacingAndGlyphs">Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr…</text>
|
||||
<text x="891" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="461" fill="#ffffff" textLength="846" lengthAdjust="spacingAndGlyphs"> Max Chat Model Attempts </text>
|
||||
<text x="855" y="461" fill="#6c7086" textLength="18" lengthAdjust="spacingAndGlyphs">10</text>
|
||||
<text x="891" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="478" fill="#6c7086" textLength="729" lengthAdjust="spacingAndGlyphs">Maximum number of attempts for requests to the main chat model. Cannot exceed 10.</text>
|
||||
<text x="891" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="512" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Debug Keystroke Logging </text>
|
||||
<text x="828" y="512" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="529" fill="#6c7086" textLength="450" lengthAdjust="spacingAndGlyphs">Enable debug logging of keystrokes to the console.</text>
|
||||
<text x="891" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="563" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▼</text>
|
||||
<text x="891" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="597" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Apply To </text>
|
||||
<text x="891" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="614" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="614" fill="#a6e3a1" textLength="117" lengthAdjust="spacingAndGlyphs">User Settings</text>
|
||||
<text x="891" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="631" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Workspace Settings </text>
|
||||
<text x="891" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="648" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> System Settings </text>
|
||||
<text x="891" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="682" fill="#6c7086" textLength="657" lengthAdjust="spacingAndGlyphs">(Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close)</text>
|
||||
<text x="891" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="716" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,131 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="751" viewBox="0 0 920 751">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="751" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="36" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> > Settings </text>
|
||||
<text x="891" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="70" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="891" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<rect x="36" y="85" width="9" height="17" fill="#ffffff" />
|
||||
<text x="36" y="87" fill="#000000" textLength="9" lengthAdjust="spacingAndGlyphs">S</text>
|
||||
<text x="45" y="87" fill="#6c7086" textLength="135" lengthAdjust="spacingAndGlyphs">earch to filter</text>
|
||||
<text x="873" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="104" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
<text x="891" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="138" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▲</text>
|
||||
<text x="891" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="155" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="155" fill="#a6e3a1" textLength="72" lengthAdjust="spacingAndGlyphs">Vim Mode</text>
|
||||
<text x="819" y="155" fill="#a6e3a1" textLength="54" lengthAdjust="spacingAndGlyphs">false*</text>
|
||||
<text x="891" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="172" fill="#6c7086" textLength="198" lengthAdjust="spacingAndGlyphs">Enable Vim keybindings</text>
|
||||
<text x="891" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="206" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> Default Approval Mode </text>
|
||||
<text x="810" y="206" fill="#6c7086" textLength="63" lengthAdjust="spacingAndGlyphs">Default</text>
|
||||
<text x="891" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="223" fill="#6c7086" textLength="738" lengthAdjust="spacingAndGlyphs">The default approval mode for tool execution. 'default' prompts for approval, 'au…</text>
|
||||
<text x="891" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="257" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Enable Auto Update true* </text>
|
||||
<text x="891" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="274" fill="#6c7086" textLength="225" lengthAdjust="spacingAndGlyphs">Enable automatic updates.</text>
|
||||
<text x="891" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="308" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Enable Notifications </text>
|
||||
<text x="828" y="308" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="325" fill="#6c7086" textLength="756" lengthAdjust="spacingAndGlyphs">Enable run-event notifications for action-required prompts and session completion. …</text>
|
||||
<text x="891" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="359" fill="#ffffff" textLength="783" lengthAdjust="spacingAndGlyphs"> Plan Directory </text>
|
||||
<text x="792" y="359" fill="#6c7086" textLength="81" lengthAdjust="spacingAndGlyphs">undefined</text>
|
||||
<text x="891" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="376" fill="#6c7086" textLength="720" lengthAdjust="spacingAndGlyphs">The directory where planning artifacts are stored. If not specified, defaults t…</text>
|
||||
<text x="891" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="410" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Plan Model Routing </text>
|
||||
<text x="837" y="410" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="427" fill="#6c7086" textLength="765" lengthAdjust="spacingAndGlyphs">Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr…</text>
|
||||
<text x="891" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="461" fill="#ffffff" textLength="846" lengthAdjust="spacingAndGlyphs"> Max Chat Model Attempts </text>
|
||||
<text x="855" y="461" fill="#6c7086" textLength="18" lengthAdjust="spacingAndGlyphs">10</text>
|
||||
<text x="891" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="478" fill="#6c7086" textLength="729" lengthAdjust="spacingAndGlyphs">Maximum number of attempts for requests to the main chat model. Cannot exceed 10.</text>
|
||||
<text x="891" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="512" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Debug Keystroke Logging false* </text>
|
||||
<text x="891" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="529" fill="#6c7086" textLength="450" lengthAdjust="spacingAndGlyphs">Enable debug logging of keystrokes to the console.</text>
|
||||
<text x="891" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="563" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▼</text>
|
||||
<text x="891" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="597" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Apply To </text>
|
||||
<text x="891" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="614" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="614" fill="#a6e3a1" textLength="117" lengthAdjust="spacingAndGlyphs">User Settings</text>
|
||||
<text x="891" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="631" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Workspace Settings </text>
|
||||
<text x="891" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="648" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> System Settings </text>
|
||||
<text x="891" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="682" fill="#6c7086" textLength="657" lengthAdjust="spacingAndGlyphs">(Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close)</text>
|
||||
<text x="891" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="716" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,133 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="751" viewBox="0 0 920 751">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="751" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="36" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> > Settings </text>
|
||||
<text x="891" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="70" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="891" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<rect x="36" y="85" width="9" height="17" fill="#ffffff" />
|
||||
<text x="36" y="87" fill="#000000" textLength="9" lengthAdjust="spacingAndGlyphs">S</text>
|
||||
<text x="45" y="87" fill="#6c7086" textLength="135" lengthAdjust="spacingAndGlyphs">earch to filter</text>
|
||||
<text x="873" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="104" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
<text x="891" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="138" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▲</text>
|
||||
<text x="891" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="155" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="155" fill="#a6e3a1" textLength="72" lengthAdjust="spacingAndGlyphs">Vim Mode</text>
|
||||
<text x="828" y="155" fill="#a6e3a1" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="172" fill="#6c7086" textLength="198" lengthAdjust="spacingAndGlyphs">Enable Vim keybindings</text>
|
||||
<text x="891" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="206" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> Default Approval Mode </text>
|
||||
<text x="810" y="206" fill="#6c7086" textLength="63" lengthAdjust="spacingAndGlyphs">Default</text>
|
||||
<text x="891" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="223" fill="#6c7086" textLength="738" lengthAdjust="spacingAndGlyphs">The default approval mode for tool execution. 'default' prompts for approval, 'au…</text>
|
||||
<text x="891" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="257" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Enable Auto Update </text>
|
||||
<text x="837" y="257" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="274" fill="#6c7086" textLength="225" lengthAdjust="spacingAndGlyphs">Enable automatic updates.</text>
|
||||
<text x="891" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="308" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Enable Notifications </text>
|
||||
<text x="828" y="308" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="325" fill="#6c7086" textLength="756" lengthAdjust="spacingAndGlyphs">Enable run-event notifications for action-required prompts and session completion. …</text>
|
||||
<text x="891" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="359" fill="#ffffff" textLength="783" lengthAdjust="spacingAndGlyphs"> Plan Directory </text>
|
||||
<text x="792" y="359" fill="#6c7086" textLength="81" lengthAdjust="spacingAndGlyphs">undefined</text>
|
||||
<text x="891" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="376" fill="#6c7086" textLength="720" lengthAdjust="spacingAndGlyphs">The directory where planning artifacts are stored. If not specified, defaults t…</text>
|
||||
<text x="891" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="410" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Plan Model Routing </text>
|
||||
<text x="837" y="410" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="427" fill="#6c7086" textLength="765" lengthAdjust="spacingAndGlyphs">Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr…</text>
|
||||
<text x="891" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="461" fill="#ffffff" textLength="846" lengthAdjust="spacingAndGlyphs"> Max Chat Model Attempts </text>
|
||||
<text x="855" y="461" fill="#6c7086" textLength="18" lengthAdjust="spacingAndGlyphs">10</text>
|
||||
<text x="891" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="478" fill="#6c7086" textLength="729" lengthAdjust="spacingAndGlyphs">Maximum number of attempts for requests to the main chat model. Cannot exceed 10.</text>
|
||||
<text x="891" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="512" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Debug Keystroke Logging </text>
|
||||
<text x="828" y="512" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="529" fill="#6c7086" textLength="450" lengthAdjust="spacingAndGlyphs">Enable debug logging of keystrokes to the console.</text>
|
||||
<text x="891" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="563" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▼</text>
|
||||
<text x="891" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="597" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Apply To </text>
|
||||
<text x="891" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="614" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="614" fill="#a6e3a1" textLength="117" lengthAdjust="spacingAndGlyphs">User Settings</text>
|
||||
<text x="891" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="631" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Workspace Settings </text>
|
||||
<text x="891" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="648" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> System Settings </text>
|
||||
<text x="891" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="682" fill="#6c7086" textLength="657" lengthAdjust="spacingAndGlyphs">(Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close)</text>
|
||||
<text x="891" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="716" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,133 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="751" viewBox="0 0 920 751">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="751" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="36" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> > Settings </text>
|
||||
<text x="891" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="70" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="891" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<rect x="36" y="85" width="9" height="17" fill="#ffffff" />
|
||||
<text x="36" y="87" fill="#000000" textLength="9" lengthAdjust="spacingAndGlyphs">S</text>
|
||||
<text x="45" y="87" fill="#6c7086" textLength="135" lengthAdjust="spacingAndGlyphs">earch to filter</text>
|
||||
<text x="873" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="104" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
<text x="891" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="138" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▲</text>
|
||||
<text x="891" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="155" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="155" fill="#a6e3a1" textLength="72" lengthAdjust="spacingAndGlyphs">Vim Mode</text>
|
||||
<text x="828" y="155" fill="#a6e3a1" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="172" fill="#6c7086" textLength="198" lengthAdjust="spacingAndGlyphs">Enable Vim keybindings</text>
|
||||
<text x="891" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="206" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> Default Approval Mode </text>
|
||||
<text x="810" y="206" fill="#6c7086" textLength="63" lengthAdjust="spacingAndGlyphs">Default</text>
|
||||
<text x="891" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="223" fill="#6c7086" textLength="738" lengthAdjust="spacingAndGlyphs">The default approval mode for tool execution. 'default' prompts for approval, 'au…</text>
|
||||
<text x="891" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="257" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Enable Auto Update </text>
|
||||
<text x="837" y="257" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="274" fill="#6c7086" textLength="225" lengthAdjust="spacingAndGlyphs">Enable automatic updates.</text>
|
||||
<text x="891" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="308" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Enable Notifications </text>
|
||||
<text x="828" y="308" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="325" fill="#6c7086" textLength="756" lengthAdjust="spacingAndGlyphs">Enable run-event notifications for action-required prompts and session completion. …</text>
|
||||
<text x="891" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="359" fill="#ffffff" textLength="783" lengthAdjust="spacingAndGlyphs"> Plan Directory </text>
|
||||
<text x="792" y="359" fill="#6c7086" textLength="81" lengthAdjust="spacingAndGlyphs">undefined</text>
|
||||
<text x="891" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="376" fill="#6c7086" textLength="720" lengthAdjust="spacingAndGlyphs">The directory where planning artifacts are stored. If not specified, defaults t…</text>
|
||||
<text x="891" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="410" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Plan Model Routing </text>
|
||||
<text x="837" y="410" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="427" fill="#6c7086" textLength="765" lengthAdjust="spacingAndGlyphs">Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr…</text>
|
||||
<text x="891" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="461" fill="#ffffff" textLength="846" lengthAdjust="spacingAndGlyphs"> Max Chat Model Attempts </text>
|
||||
<text x="855" y="461" fill="#6c7086" textLength="18" lengthAdjust="spacingAndGlyphs">10</text>
|
||||
<text x="891" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="478" fill="#6c7086" textLength="729" lengthAdjust="spacingAndGlyphs">Maximum number of attempts for requests to the main chat model. Cannot exceed 10.</text>
|
||||
<text x="891" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="512" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Debug Keystroke Logging </text>
|
||||
<text x="828" y="512" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="529" fill="#6c7086" textLength="450" lengthAdjust="spacingAndGlyphs">Enable debug logging of keystrokes to the console.</text>
|
||||
<text x="891" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="563" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▼</text>
|
||||
<text x="891" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="597" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Apply To </text>
|
||||
<text x="891" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="614" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="614" fill="#a6e3a1" textLength="117" lengthAdjust="spacingAndGlyphs">User Settings</text>
|
||||
<text x="891" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="631" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Workspace Settings </text>
|
||||
<text x="891" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="648" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> System Settings </text>
|
||||
<text x="891" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="682" fill="#6c7086" textLength="657" lengthAdjust="spacingAndGlyphs">(Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close)</text>
|
||||
<text x="891" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="716" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,131 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="751" viewBox="0 0 920 751">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="751" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="36" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Settings </text>
|
||||
<text x="891" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="70" fill="#3d3f51" textLength="864" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="891" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="36" y="87" fill="#6c7086" textLength="144" lengthAdjust="spacingAndGlyphs">Search to filter</text>
|
||||
<text x="873" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="104" fill="#3d3f51" textLength="864" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
<text x="891" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="138" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▲</text>
|
||||
<text x="891" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="155" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Vim Mode </text>
|
||||
<text x="828" y="155" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="172" fill="#6c7086" textLength="198" lengthAdjust="spacingAndGlyphs">Enable Vim keybindings</text>
|
||||
<text x="891" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="206" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> Default Approval Mode </text>
|
||||
<text x="810" y="206" fill="#6c7086" textLength="63" lengthAdjust="spacingAndGlyphs">Default</text>
|
||||
<text x="891" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="223" fill="#6c7086" textLength="738" lengthAdjust="spacingAndGlyphs">The default approval mode for tool execution. 'default' prompts for approval, 'au…</text>
|
||||
<text x="891" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="257" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Enable Auto Update </text>
|
||||
<text x="837" y="257" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="274" fill="#6c7086" textLength="225" lengthAdjust="spacingAndGlyphs">Enable automatic updates.</text>
|
||||
<text x="891" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="308" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Enable Notifications </text>
|
||||
<text x="828" y="308" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="325" fill="#6c7086" textLength="756" lengthAdjust="spacingAndGlyphs">Enable run-event notifications for action-required prompts and session completion. …</text>
|
||||
<text x="891" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="359" fill="#ffffff" textLength="783" lengthAdjust="spacingAndGlyphs"> Plan Directory </text>
|
||||
<text x="792" y="359" fill="#6c7086" textLength="81" lengthAdjust="spacingAndGlyphs">undefined</text>
|
||||
<text x="891" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="376" fill="#6c7086" textLength="720" lengthAdjust="spacingAndGlyphs">The directory where planning artifacts are stored. If not specified, defaults t…</text>
|
||||
<text x="891" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="410" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Plan Model Routing </text>
|
||||
<text x="837" y="410" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="427" fill="#6c7086" textLength="765" lengthAdjust="spacingAndGlyphs">Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr…</text>
|
||||
<text x="891" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="461" fill="#ffffff" textLength="846" lengthAdjust="spacingAndGlyphs"> Max Chat Model Attempts </text>
|
||||
<text x="855" y="461" fill="#6c7086" textLength="18" lengthAdjust="spacingAndGlyphs">10</text>
|
||||
<text x="891" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="478" fill="#6c7086" textLength="729" lengthAdjust="spacingAndGlyphs">Maximum number of attempts for requests to the main chat model. Cannot exceed 10.</text>
|
||||
<text x="891" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="512" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Debug Keystroke Logging </text>
|
||||
<text x="828" y="512" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="529" fill="#6c7086" textLength="450" lengthAdjust="spacingAndGlyphs">Enable debug logging of keystrokes to the console.</text>
|
||||
<text x="891" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="563" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▼</text>
|
||||
<text x="891" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="597" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> > Apply To </text>
|
||||
<text x="891" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="614" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="614" fill="#a6e3a1" textLength="18" lengthAdjust="spacingAndGlyphs">1.</text>
|
||||
<text x="72" y="614" fill="#a6e3a1" textLength="117" lengthAdjust="spacingAndGlyphs">User Settings</text>
|
||||
<text x="891" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="631" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> 2. Workspace Settings </text>
|
||||
<text x="891" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="648" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> 3. System Settings </text>
|
||||
<text x="891" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="682" fill="#6c7086" textLength="657" lengthAdjust="spacingAndGlyphs">(Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close)</text>
|
||||
<text x="891" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="716" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,132 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="751" viewBox="0 0 920 751">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="751" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="36" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> > Settings </text>
|
||||
<text x="891" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="70" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="891" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<rect x="36" y="85" width="9" height="17" fill="#ffffff" />
|
||||
<text x="36" y="87" fill="#000000" textLength="9" lengthAdjust="spacingAndGlyphs">S</text>
|
||||
<text x="45" y="87" fill="#6c7086" textLength="135" lengthAdjust="spacingAndGlyphs">earch to filter</text>
|
||||
<text x="873" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="104" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
<text x="891" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="138" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▲</text>
|
||||
<text x="891" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="155" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="155" fill="#a6e3a1" textLength="72" lengthAdjust="spacingAndGlyphs">Vim Mode</text>
|
||||
<text x="819" y="155" fill="#a6e3a1" textLength="54" lengthAdjust="spacingAndGlyphs">false*</text>
|
||||
<text x="891" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="172" fill="#6c7086" textLength="198" lengthAdjust="spacingAndGlyphs">Enable Vim keybindings</text>
|
||||
<text x="891" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="206" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> Default Approval Mode </text>
|
||||
<text x="810" y="206" fill="#6c7086" textLength="63" lengthAdjust="spacingAndGlyphs">Default</text>
|
||||
<text x="891" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="223" fill="#6c7086" textLength="738" lengthAdjust="spacingAndGlyphs">The default approval mode for tool execution. 'default' prompts for approval, 'au…</text>
|
||||
<text x="891" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="257" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Enable Auto Update false* </text>
|
||||
<text x="891" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="274" fill="#6c7086" textLength="225" lengthAdjust="spacingAndGlyphs">Enable automatic updates.</text>
|
||||
<text x="891" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="308" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Enable Notifications </text>
|
||||
<text x="828" y="308" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="325" fill="#6c7086" textLength="756" lengthAdjust="spacingAndGlyphs">Enable run-event notifications for action-required prompts and session completion. …</text>
|
||||
<text x="891" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="359" fill="#ffffff" textLength="783" lengthAdjust="spacingAndGlyphs"> Plan Directory </text>
|
||||
<text x="792" y="359" fill="#6c7086" textLength="81" lengthAdjust="spacingAndGlyphs">undefined</text>
|
||||
<text x="891" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="376" fill="#6c7086" textLength="720" lengthAdjust="spacingAndGlyphs">The directory where planning artifacts are stored. If not specified, defaults t…</text>
|
||||
<text x="891" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="410" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Plan Model Routing </text>
|
||||
<text x="837" y="410" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="427" fill="#6c7086" textLength="765" lengthAdjust="spacingAndGlyphs">Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr…</text>
|
||||
<text x="891" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="461" fill="#ffffff" textLength="846" lengthAdjust="spacingAndGlyphs"> Max Chat Model Attempts </text>
|
||||
<text x="855" y="461" fill="#6c7086" textLength="18" lengthAdjust="spacingAndGlyphs">10</text>
|
||||
<text x="891" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="478" fill="#6c7086" textLength="729" lengthAdjust="spacingAndGlyphs">Maximum number of attempts for requests to the main chat model. Cannot exceed 10.</text>
|
||||
<text x="891" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="512" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Debug Keystroke Logging </text>
|
||||
<text x="828" y="512" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="529" fill="#6c7086" textLength="450" lengthAdjust="spacingAndGlyphs">Enable debug logging of keystrokes to the console.</text>
|
||||
<text x="891" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="563" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▼</text>
|
||||
<text x="891" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="597" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Apply To </text>
|
||||
<text x="891" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="614" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="614" fill="#a6e3a1" textLength="117" lengthAdjust="spacingAndGlyphs">User Settings</text>
|
||||
<text x="891" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="631" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Workspace Settings </text>
|
||||
<text x="891" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="648" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> System Settings </text>
|
||||
<text x="891" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="682" fill="#6c7086" textLength="657" lengthAdjust="spacingAndGlyphs">(Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close)</text>
|
||||
<text x="891" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="716" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,133 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="751" viewBox="0 0 920 751">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="751" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="36" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> > Settings </text>
|
||||
<text x="891" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="70" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="891" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<rect x="36" y="85" width="9" height="17" fill="#ffffff" />
|
||||
<text x="36" y="87" fill="#000000" textLength="9" lengthAdjust="spacingAndGlyphs">S</text>
|
||||
<text x="45" y="87" fill="#6c7086" textLength="135" lengthAdjust="spacingAndGlyphs">earch to filter</text>
|
||||
<text x="873" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="104" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
<text x="891" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="138" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▲</text>
|
||||
<text x="891" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="155" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="155" fill="#a6e3a1" textLength="72" lengthAdjust="spacingAndGlyphs">Vim Mode</text>
|
||||
<text x="828" y="155" fill="#a6e3a1" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="172" fill="#6c7086" textLength="198" lengthAdjust="spacingAndGlyphs">Enable Vim keybindings</text>
|
||||
<text x="891" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="206" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> Default Approval Mode </text>
|
||||
<text x="810" y="206" fill="#6c7086" textLength="63" lengthAdjust="spacingAndGlyphs">Default</text>
|
||||
<text x="891" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="223" fill="#6c7086" textLength="738" lengthAdjust="spacingAndGlyphs">The default approval mode for tool execution. 'default' prompts for approval, 'au…</text>
|
||||
<text x="891" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="257" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Enable Auto Update </text>
|
||||
<text x="837" y="257" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="274" fill="#6c7086" textLength="225" lengthAdjust="spacingAndGlyphs">Enable automatic updates.</text>
|
||||
<text x="891" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="308" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Enable Notifications </text>
|
||||
<text x="828" y="308" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="325" fill="#6c7086" textLength="756" lengthAdjust="spacingAndGlyphs">Enable run-event notifications for action-required prompts and session completion. …</text>
|
||||
<text x="891" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="359" fill="#ffffff" textLength="783" lengthAdjust="spacingAndGlyphs"> Plan Directory </text>
|
||||
<text x="792" y="359" fill="#6c7086" textLength="81" lengthAdjust="spacingAndGlyphs">undefined</text>
|
||||
<text x="891" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="376" fill="#6c7086" textLength="720" lengthAdjust="spacingAndGlyphs">The directory where planning artifacts are stored. If not specified, defaults t…</text>
|
||||
<text x="891" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="410" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Plan Model Routing </text>
|
||||
<text x="837" y="410" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="427" fill="#6c7086" textLength="765" lengthAdjust="spacingAndGlyphs">Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr…</text>
|
||||
<text x="891" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="461" fill="#ffffff" textLength="846" lengthAdjust="spacingAndGlyphs"> Max Chat Model Attempts </text>
|
||||
<text x="855" y="461" fill="#6c7086" textLength="18" lengthAdjust="spacingAndGlyphs">10</text>
|
||||
<text x="891" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="478" fill="#6c7086" textLength="729" lengthAdjust="spacingAndGlyphs">Maximum number of attempts for requests to the main chat model. Cannot exceed 10.</text>
|
||||
<text x="891" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="512" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Debug Keystroke Logging </text>
|
||||
<text x="828" y="512" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="529" fill="#6c7086" textLength="450" lengthAdjust="spacingAndGlyphs">Enable debug logging of keystrokes to the console.</text>
|
||||
<text x="891" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="563" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▼</text>
|
||||
<text x="891" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="597" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Apply To </text>
|
||||
<text x="891" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="614" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="614" fill="#a6e3a1" textLength="117" lengthAdjust="spacingAndGlyphs">User Settings</text>
|
||||
<text x="891" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="631" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Workspace Settings </text>
|
||||
<text x="891" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="648" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> System Settings </text>
|
||||
<text x="891" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="682" fill="#6c7086" textLength="657" lengthAdjust="spacingAndGlyphs">(Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close)</text>
|
||||
<text x="891" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="716" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,131 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="751" viewBox="0 0 920 751">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="751" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="19" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="36" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> > Settings </text>
|
||||
<text x="891" y="36" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="53" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="70" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="891" y="70" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<rect x="36" y="85" width="9" height="17" fill="#ffffff" />
|
||||
<text x="36" y="87" fill="#000000" textLength="9" lengthAdjust="spacingAndGlyphs">S</text>
|
||||
<text x="45" y="87" fill="#6c7086" textLength="135" lengthAdjust="spacingAndGlyphs">earch to filter</text>
|
||||
<text x="873" y="87" fill="#89b4fa" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="87" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="104" fill="#89b4fa" textLength="864" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
<text x="891" y="104" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="121" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="138" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▲</text>
|
||||
<text x="891" y="138" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="155" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="155" fill="#a6e3a1" textLength="72" lengthAdjust="spacingAndGlyphs">Vim Mode</text>
|
||||
<text x="828" y="155" fill="#a6e3a1" textLength="45" lengthAdjust="spacingAndGlyphs">true*</text>
|
||||
<text x="891" y="155" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="172" fill="#6c7086" textLength="198" lengthAdjust="spacingAndGlyphs">Enable Vim keybindings</text>
|
||||
<text x="891" y="172" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="189" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="206" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> Default Approval Mode </text>
|
||||
<text x="810" y="206" fill="#6c7086" textLength="63" lengthAdjust="spacingAndGlyphs">Default</text>
|
||||
<text x="891" y="206" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="223" fill="#6c7086" textLength="738" lengthAdjust="spacingAndGlyphs">The default approval mode for tool execution. 'default' prompts for approval, 'au…</text>
|
||||
<text x="891" y="223" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="240" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="257" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Enable Auto Update false* </text>
|
||||
<text x="891" y="257" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="274" fill="#6c7086" textLength="225" lengthAdjust="spacingAndGlyphs">Enable automatic updates.</text>
|
||||
<text x="891" y="274" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="291" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="308" fill="#ffffff" textLength="819" lengthAdjust="spacingAndGlyphs"> Enable Notifications </text>
|
||||
<text x="828" y="308" fill="#6c7086" textLength="45" lengthAdjust="spacingAndGlyphs">false</text>
|
||||
<text x="891" y="308" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="325" fill="#6c7086" textLength="756" lengthAdjust="spacingAndGlyphs">Enable run-event notifications for action-required prompts and session completion. …</text>
|
||||
<text x="891" y="325" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="342" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="359" fill="#ffffff" textLength="783" lengthAdjust="spacingAndGlyphs"> Plan Directory </text>
|
||||
<text x="792" y="359" fill="#6c7086" textLength="81" lengthAdjust="spacingAndGlyphs">undefined</text>
|
||||
<text x="891" y="359" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="376" fill="#6c7086" textLength="720" lengthAdjust="spacingAndGlyphs">The directory where planning artifacts are stored. If not specified, defaults t…</text>
|
||||
<text x="891" y="376" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="393" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="410" fill="#ffffff" textLength="828" lengthAdjust="spacingAndGlyphs"> Plan Model Routing </text>
|
||||
<text x="837" y="410" fill="#6c7086" textLength="36" lengthAdjust="spacingAndGlyphs">true</text>
|
||||
<text x="891" y="410" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="427" fill="#6c7086" textLength="765" lengthAdjust="spacingAndGlyphs">Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr…</text>
|
||||
<text x="891" y="427" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="444" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="461" fill="#ffffff" textLength="846" lengthAdjust="spacingAndGlyphs"> Max Chat Model Attempts </text>
|
||||
<text x="855" y="461" fill="#6c7086" textLength="18" lengthAdjust="spacingAndGlyphs">10</text>
|
||||
<text x="891" y="461" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="478" fill="#6c7086" textLength="729" lengthAdjust="spacingAndGlyphs">Maximum number of attempts for requests to the main chat model. Cannot exceed 10.</text>
|
||||
<text x="891" y="478" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="495" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="512" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Debug Keystroke Logging true* </text>
|
||||
<text x="891" y="512" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="45" y="529" fill="#6c7086" textLength="450" lengthAdjust="spacingAndGlyphs">Enable debug logging of keystrokes to the console.</text>
|
||||
<text x="891" y="529" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="546" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="563" fill="#6c7086" textLength="9" lengthAdjust="spacingAndGlyphs">▼</text>
|
||||
<text x="891" y="563" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="580" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="597" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Apply To </text>
|
||||
<text x="891" y="597" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="614" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">●</text>
|
||||
<text x="45" y="614" fill="#a6e3a1" textLength="117" lengthAdjust="spacingAndGlyphs">User Settings</text>
|
||||
<text x="891" y="614" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="631" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> Workspace Settings </text>
|
||||
<text x="891" y="631" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="9" y="648" fill="#ffffff" textLength="882" lengthAdjust="spacingAndGlyphs"> System Settings </text>
|
||||
<text x="891" y="648" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="665" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="27" y="682" fill="#6c7086" textLength="657" lengthAdjust="spacingAndGlyphs">(Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close)</text>
|
||||
<text x="891" y="682" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="891" y="699" fill="#3d3f51" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="716" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -43,8 +43,7 @@ exports[`SettingsDialog > Initial Rendering > should render settings list with v
|
||||
│ │
|
||||
│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'accessibility settings enabled' correctly 1`] = `
|
||||
@@ -90,8 +89,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'accessibility settings
|
||||
│ │
|
||||
│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'all boolean settings disabled' correctly 1`] = `
|
||||
@@ -137,8 +135,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'all boolean settings d
|
||||
│ │
|
||||
│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'default state' correctly 1`] = `
|
||||
@@ -184,8 +181,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'default state' correct
|
||||
│ │
|
||||
│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'file filtering settings configured' correctly 1`] = `
|
||||
@@ -231,8 +227,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'file filtering setting
|
||||
│ │
|
||||
│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'focused on scope selector' correctly 1`] = `
|
||||
@@ -278,8 +273,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'focused on scope selec
|
||||
│ │
|
||||
│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'mixed boolean and number settings' correctly 1`] = `
|
||||
@@ -325,8 +319,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'mixed boolean and numb
|
||||
│ │
|
||||
│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'tools and security settings' correctly 1`] = `
|
||||
@@ -372,8 +365,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'tools and security set
|
||||
│ │
|
||||
│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'various boolean settings enabled' correctly 1`] = `
|
||||
@@ -419,6 +411,5 @@ exports[`SettingsDialog > Snapshot Tests > should render 'various boolean settin
|
||||
│ │
|
||||
│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="88" viewBox="0 0 920 88">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="88" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">ID Name </text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">────────────────────────────────────────────────────────────────────────────────────────────────────</text>
|
||||
<text x="0" y="36" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">1 Alice </text>
|
||||
<text x="0" y="53" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">2 Bob </text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="71" viewBox="0 0 920 71">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="71" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Value </text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">────────────────────────────────────────────────────────────────────────────────────────────────────</text>
|
||||
<text x="0" y="36" fill="#00cd00" textLength="18" lengthAdjust="spacingAndGlyphs">20</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="71" viewBox="0 0 920 71">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="71" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Status </text>
|
||||
<text x="0" y="19" fill="#3d3f51" textLength="900" lengthAdjust="spacingAndGlyphs">────────────────────────────────────────────────────────────────────────────────────────────────────</text>
|
||||
<rect x="0" y="34" width="54" height="17" fill="#ffffff" />
|
||||
<text x="0" y="36" fill="#000000" textLength="54" lengthAdjust="spacingAndGlyphs">Active</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |