Skip to content

settings.json is rewritten with different formatting even when logical content hasn't changed #18934

@TyceHerrman

Description

@TyceHerrman

Title

settings.json is rewritten with different formatting even when logical content hasn't changed

Description

When Gemini CLI writes settings.json (either ~/.gemini/settings.json or project-level .gemini/settings.json), it rewrites the file even when the logical content hasn't changed. The rewrite also produces formatting that differs from the original file, causing the file to appear as modified in git.

The problem

The write path in commentJson.ts uses comment-json's stringify(obj, null, 2), which:

  1. Always expands arrays vertically — e.g. ["AGENTS.md", "GEMINI.md"] becomes:
    [
      "AGENTS.md",
      "GEMINI.md"
    ]
    
  2. Does not append a trailing newline — most formatters (prettier, editors) expect a final \n

This means any project that runs prettier (or similar formatters) on JSON files will get a formatting conflict every time Gemini CLI touches settings.json. The file flip-flops between the formatter's output and Gemini CLI's output, showing up as a dirty file in git.

Steps to reproduce

  1. Create a .gemini/settings.json formatted by prettier (short arrays on one line, trailing newline):
    {
      "context": {
        "fileName": ["AGENTS.md", "GEMINI.md"]
      }
    }
  2. Run gemini in the project directory
  3. Check git diff .gemini/settings.json — the array is now expanded vertically and/or the trailing newline is removed

Expected behavior

If the logical content of settings.json hasn't changed, the file should not be rewritten. A simple deep-equality check before writing would prevent unnecessary rewrites and preserve the user's existing formatting.

Suggested fix

In updateSettingsFilePreservingFormat(), compare the parsed existing content with the new content before writing:

const existingContent = fs.readFileSync(filePath, "utf-8");
const existingParsed = parse(existingContent);
const updatedStructure = deepMerge(existingParsed, updates);

// Skip write if nothing changed
if (JSON.stringify(existingParsed) === JSON.stringify(updatedStructure)) {
  return;
}

This would also address the symlink destruction reported in #10960, since unnecessary writes are what trigger the symlink replacement.

Environment

  • Gemini CLI version: latest (installed via npm)
  • OS: macOS
  • Project uses prettier with printWidth: 88

Related issues

Metadata

Metadata

Assignees

Labels

area/coreIssues related to User Interface, OS Support, Core Functionalitypriority/p3Backlog - a good idea but not currently a priority.type/bug🔒 maintainer only⛔ Do not contribute. Internal roadmap item.

Type

No fields configured for Bug.

Projects

Status

Closed

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions