Skip to content

Conversation

@ndbroadbent
Copy link
Member

@ndbroadbent ndbroadbent commented Sep 1, 2025

  • 79a8e21 - (HEAD -> nathan/atomic-identifiers, origin/nathan/atomic-identifiers) never use lower/upper for the replacement text style, it destroys word boundaries (2 minutes ago)
  • 797fb41 - fix dot test during e2e (28 minutes ago)
  • 6b5fde2 - Fix ambiguity resolution to use replacement text style as fallback (31 minutes ago)
  • 70df3d1 - Fix ambiguity resolution and exact match preservation (57 minutes ago)
  • a29719e - ensure binary name case is preserved in Cargo.toml (2 hours ago)
  • 2e604aa - add renamify-cli/.gitignore (2 hours ago)
  • 4f931d4 - feat: improve exact match preservation and acronym tokenization (2 hours ago)
  • b56c59c - docs: add atomic mode documentation for all commands and MCP tools (3 hours ago)
  • c1f2afd - feat: complete atomic identifiers feature with VS Code UI improvements (3 hours ago)
  • ae2760b - docs: add Atomic Identifiers feature documentation (6 hours ago)
  • d8dca9f - feat(vscode): add atomic mode checkboxes to UI (6 hours ago)
  • ea177d3 - feat(mcp): add atomic parameters to MCP tools (6 hours ago)
  • f604647 - feat: add atomic identifier support (6 hours ago)
  • 8b25e03 - fixed config path (6 hours ago)

Summary by CodeRabbit

  • New Features
    • Atomic Identifiers mode: CLI flags, project config (.renamify/config.toml), MCP tool options, and VS Code checkboxes to treat selected identifiers as indivisible for search/replace.
  • Improvements
    • Smarter casing/ambiguity resolution and better acronym preservation across renames.
  • Documentation
    • New Atomic Identifiers feature page, updated command docs, examples, navigation, and VS Code tooltips.
  • Tests
    • New integration and unit tests covering atomic behavior, casing preservation, and ambiguity scenarios.
  • Chores
    • Ignore .renamify workspace directory.

ndbroadbent and others added 14 commits September 1, 2025 21:11
- Add --atomic, --atomic-search, --atomic-replace CLI flags
- Support atomic identifiers in .renamify/config.toml
- Treat atomic identifiers as indivisible units during case transformations
- Add comprehensive tests for atomic mode
- Update all existing tests to handle atomic_config parameter

Examples:
- DocSpring (atomic) → docspring in snake_case (not doc_spring)
- FormAPIController → DocSpringController when FormAPI is atomic
- FORMAPI_TOKEN → DOCSPRING_TOKEN preserving atomic boundaries
- Add atomicSearch and atomicReplace parameters to plan, search, and rename tools
- Update TypeScript interfaces with atomic options
- Pass atomic flags to CLI commands when specified
- All existing tests pass
- Add atomicSearch and atomicReplace checkboxes with tooltips
- Update types to include atomic options
- Pass atomic flags to CLI commands when checked
- UI checkboxes explain 'indivisible unit (no word boundaries)'
- Create comprehensive documentation page for atomic identifiers
- Explain use cases, CLI flags, and configuration options
- Include real-world examples and best practices
- Add to sidebar navigation under Features section
- Added atomic mode checkboxes to VS Code extension on same line
- Implemented CSS-based tooltips with clear examples (DocSpring → docspring)
- Fixed tooltip positioning to avoid screen edge cutoffs
- Added "more info" link for ambiguous identifiers documentation
- All checkboxes now trigger search re-run when toggled
- Checkbox states persist when webview is hidden/shown
- Fixed test assertions to match correct atomic behavior
- Updated documentation with proper MDX formatting

The atomic feature now properly treats compound brand names as indivisible
units during case transformations across CLI, MCP server, and VS Code extension.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Updated search, plan, and rename command docs with atomic flags
- Added atomic parameters to MCP tool documentation
- Added examples showing atomic mode usage
- Documented both --atomic-search and --atomic-replace options

Note: Testing revealed atomic mode is not fully working yet - produces
FormApi instead of FormAPI. Implementation needs fixing.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Add exact match preservation to maintain user's exact casing (e.g., FormAPI stays FormAPI, not FormApi)
- Only apply exact match when using default styles, not with --only-styles flag
- Improve acronym boundary detection for compound words (e.g., APIClient → API + Client)
- Handle consecutive acronyms correctly (e.g., HTTPSAPIClient → HTTPS + API + Client)
- Add case consistency check to avoid false acronym matches (e.g., 'Cli' is not 'CLI')
- Preserve known acronyms in PascalCase and camelCase outputs

This ensures that compound brand names and technical terms with acronyms are preserved exactly as the user types them, while still generating appropriate variants for different case styles.
- Wire up ambiguity resolver in scanner.rs to actually use the extensive ambiguity resolution code
- Fix ambiguity detection to be more precise:
 - PascalCase requires no separators (not just starting with uppercase)
 - Train-Case requires hyphens or proper Title/acronym segments
 - Title Case requires spaces (not just starting with uppercase)
 - ScreamingTrain requires hyphens (not just all uppercase)
- Add integration tests for ambiguity resolution in Python and JavaScript contexts
- Fix binary name preservation test to find correct line numbers
- Add debug logging to language heuristics for troubleshooting
- Ensure exact match preservation works for non-ambiguous mixed-case identifiers

The key fix is that ambiguous identifiers (like 'api', 'API') now use context-aware resolution
to determine the appropriate case style based on language-specific heuristics, while
non-ambiguous identifiers (like 'OAuth', 'WeIrDCaSe') preserve exact user casing.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
When ambiguous identifiers like 'renamify' are found in contexts where language
heuristics don't provide a clear style preference (like TOML package names),
the resolver now uses the style of the replacement text as a hint.

Key changes:
- Pass original replacement text to ambiguity resolver (not the styled variant)
- Calculate replacement possible styles once per file for performance
- In default_fallback, find intersection of matched and replacement styles
- If replacement is snake_case (awesome_file_renaming_tool), prefer that

This fixes the e2e test where binary names were becoming camelCase instead
of preserving the snake_case from the replacement string.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Sep 1, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds Atomic Identifiers feature across core, CLI, MCP, and VS Code: new atomic config, parsing, and variant generation; ambiguity resolver enhancements; scanner integration; CLI flags and wiring; MCP and VS Code options/UX; extensive documentation; tests added and updated; config gains atomic list.

Changes

Cohort / File(s) Summary
PRD & High-level Docs
.taskmaster/docs/atomic-prd.txt, CLAUDE.md
Adds a Product Requirements Document for Atomic Identifiers and documents .renamify/config.toml atomic list semantics.
Docs Site: Nav & Features
docs/astro.config.mjs, docs/src/content/docs/features/atomic-identifiers.mdx
Adds “Atomic Identifiers” feature page and navigation entry; new feature documentation and usage guidance.
Docs: CLI Commands
docs/src/content/docs/commands/plan.mdx, docs/src/content/docs/commands/rename.mdx, docs/src/content/docs/commands/search.mdx
Documents Atomic Mode and six flags (--atomic, --atomic-search, --atomic-replace, --no-atomic, --no-atomic-search, --no-atomic-replace) with examples and duplicated example blocks in some pages.
Docs: MCP Tools
docs/src/content/docs/mcp/tools.mdx
Adds atomicSearch/atomicReplace flags and examples to MCP tool docs; updates config/status examples.
CLI: Args & Wiring
renamify-cli/src/cli/args.rs, renamify-cli/src/main.rs, renamify-cli/src/plan.rs, renamify-cli/src/replace.rs, renamify-cli/.gitignore
Introduces AtomicArgs struct with flags; flattens into Rename/Plan/Apply commands; threads atomic args into plan flow; Search constructs defaults; Replace sets atomic_config: None; adds .renamify/ to .gitignore.
Core: Atomic Module & Config
renamify-core/src/atomic.rs, renamify-core/src/config.rs, renamify-core/src/lib.rs
Adds atomic module with AtomicConfig, parsing, style conversion and variant generation helpers; adds Config.atomic: Vec<String> and exports pub mod atomic;.
Core: Case Modeling & Scanner
renamify-core/src/case_model.rs, renamify-core/src/scanner.rs, renamify-core/src/operations/plan.rs, renamify-core/src/operations/rename.rs
Adds atomic-aware variant generation (generate_variant_map_with_atomic), tokens/acronym refinements, threads atomic_config through PlanOptions, scanner uses atomic map when provided, integrates ambiguity resolution with precomputed replacement styles, and updates generate_hunks signature.
Core: Ambiguity Heuristics & Resolver
renamify-core/src/ambiguity/mod.rs, renamify-core/src/ambiguity/language_heuristics.rs, renamify-core/src/ambiguity/resolver.rs
Tightens style-detection heuristics, expands debug logging, adds cross-file context step and resolve_with_styles API, and refines fallback precedence (removes Lower/Upper from precedence).
MCP Service
renamify-mcp/src/index.ts, renamify-mcp/src/renamify-service.ts
Adds atomicSearch/atomicReplace input schema fields and wires flags through service/CLI invocation; updates Plan/Search/Rename option shapes.
VS Code Extension: UI & Types
renamify-vscode/extension/src/cliService.ts, renamify-vscode/extension/src/types.ts, renamify-vscode/extension/templates/webview.hbs, renamify-vscode/media/style.css, renamify-vscode/webview/src/webview.ts
Adds atomic search/replace checkboxes, tooltips, CSS for tooltip system, state persistence, types for new booleans, and pipes flags into CLI calls.
CLI Tests: New & Updated
renamify-cli/tests/binary_name_preservation.rs, renamify-cli/tests/cli_tests.rs
Adds binary name preservation integration tests; updates CLI tests to pass new atomic_config (None) where required.
Core Tests: New & Updated
renamify-core/tests/*.rs (many files; see summary)
Adds ambiguity_integration, exact_match_preserve, format_string tests; updates numerous tests to initialize PlanOptions.atomic_config: None; adds/adjusts other test expectations where needed.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as Renamify CLI
  participant Core as renamify_core::plan_operation
  participant Scan as Scanner
  participant Amb as AmbiguityResolver

  User->>CLI: plan --search S --replace R [--atomic*]
  CLI->>Core: plan_operation(S, R, ..., atomic_config)
  Core->>Scan: scan_repository_multi(..., atomic_config)
  alt atomic_config present
    Scan->>Scan: generate_variant_map_with_atomic(S, R)
  else non-atomic
    Scan->>Scan: generate_variant_map_with_acronyms(S, R)
  end
  loop For each match
    alt ambiguous variant
      Scan->>Amb: resolve_with_styles(matched, R, context, precomputed R styles)
      Amb-->>Scan: ResolvedStyle
      Scan->>Scan: restyle R tokens -> styled_replacement
    else compound/exact
      Scan->>Scan: use variant_map replacement
    end
  end
  Scan-->>Core: Plan with matches
  Core-->>CLI: PlanResult
  CLI-->>User: Preview/JSON
Loading
sequenceDiagram
  autonumber
  actor Dev as VS Code User
  participant UI as VS Code Webview
  participant Ext as VS Code Extension
  participant MCP as MCP Service
  participant CLI as Renamify CLI

  Dev->>UI: Toggle Atomic Search/Replace
  UI->>Ext: postMessage{atomicSearch, atomicReplace}
  Ext->>MCP: createPlan(options with atomic*)
  MCP->>CLI: renamify plan --atomic-search/--atomic-replace
  CLI-->>MCP: Plan JSON
  MCP-->>Ext: Plan result
  Ext-->>UI: Render plan
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

Hops on the keys, my whiskers twitch—
“Atomic!” I squeak, with a coder’s itch.
No more split of Git and Hub,
DocSpring stays a single nub.
Flags aflutter, plans align,
I thump approval—search is fine.
Carrots case-closed: one tidy line.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 79a8e21 and 19e4a4f.

📒 Files selected for processing (4)
  • docs/src/content/docs/commands/plan.mdx (2 hunks)
  • docs/src/content/docs/commands/rename.mdx (2 hunks)
  • renamify-cli/src/cli/args.rs (3 hunks)
  • renamify-mcp/src/index.ts (3 hunks)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link

codecov bot commented Sep 1, 2025

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

@ndbroadbent ndbroadbent merged commit 5348815 into main Sep 1, 2025
16 checks passed
@ndbroadbent ndbroadbent deleted the nathan/atomic-identifiers branch September 1, 2025 15:21
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
renamify-core/tests/hyphenated_capitalized_test.rs (1)

45-45: Ensure plan files are written inside the temp dir

Using a bare "plan.json" risks writing to the process CWD if scan_repository doesn’t relocate paths. Safer to write into the test sandbox.

-        plan_out: PathBuf::from("plan.json"),
+        plan_out: root.join("plan.json"),

Also applies to: 155-155, 237-237, 313-313, 391-391

renamify-cli/src/replace.rs (1)

143-154: Avoid cloning Plan before apply; pass &mut plan directly.

Cloning can be very expensive on large plans and discards any in-place updates made during apply.

Apply:

-    let plan = if no_regex {
+    let mut plan = if no_regex {
         // Literal string replacement
         create_simple_plan(pattern, replacement, paths, &options, false)?
     } else {
         // Regex replacement - validate the pattern first
         Regex::new(pattern).with_context(|| format!("Invalid regex pattern: {}", pattern))?;

         // Create plan with regex replacement
         create_simple_plan(pattern, replacement, paths, &options, true)?
     };
@@
-    apply_plan(&mut plan.clone(), &apply_options)?;
+    apply_plan(&mut plan, &apply_options)?;
renamify-vscode/webview/src/webview.ts (1)

350-358: Keep openPreview in sync with search/apply (missing flags).

openPreview doesn’t pass renamePaths/ignoreAmbiguous/atomic* so preview can diverge from search/apply results. Add the four booleans.

   vscode.postMessage({
     type: 'openPreview',
     search: searchTerm,
     replace: replaceTerm,
     include: includeInput.value,
     exclude: excludeInput.value,
     excludeMatchingLines: excludeLinesInput.value,
-    caseStyles: selectedStyles,
+    caseStyles: selectedStyles,
+    renamePaths: renamePathsCheckbox?.checked ?? false,
+    ignoreAmbiguous: ignoreAmbiguousCheckbox?.checked ?? false,
+    atomicSearch: atomicSearchCheckbox?.checked ?? false,
+    atomicReplace: atomicReplaceCheckbox?.checked ?? false,
   });
renamify-cli/src/main.rs (1)

222-241: Reject unsupported atomic flags in rename
rename silently ignores the --atomic* flags (they’re destructured as _), which is confusing. Explicitly refuse them until support is implemented.

In the Commands::Rename arm in main.rs, update:

-            atomic: _, // Not implemented in rename yet
+            atomic,

and immediately after the match add:

if atomic.atomic
    || atomic.atomic_search
    || atomic.atomic_replace
    || atomic.no_atomic
    || atomic.no_atomic_search
    || atomic.no_atomic_replace
{
    eprintln!("--atomic* flags are not yet supported in `rename`. Use `plan` + `apply` for now.");
    process::exit(2);
}
renamify-core/src/ambiguity/resolver.rs (2)

160-166: Potential UTF-8 panic: slicing with byte offsets on &str

match_position is a byte offset. Using &line[..match_pos] panics on non–char-boundary indices.

-        let preceding = if match_pos > 0 {
-            &line[..match_pos]
-        } else {
-            ""
-        };
+        let preceding = line.get(..match_pos).unwrap_or("");

Apply the same fix in try_cross_file_context.

Also applies to: 225-231


369-375: Don’t fall back to Style::Lower; it contradicts “never use lower/upper” policy

Lower/Upper destroy word boundaries. Return a safe style (e.g., Snake) instead.

-        ResolvedStyle {
-            style: Style::Lower,
-            confidence: ResolutionConfidence::Low,
-            method: ResolutionMethod::DefaultFallback,
-        }
+        ResolvedStyle {
+            style: Style::Snake,
+            confidence: ResolutionConfidence::Low,
+            method: ResolutionMethod::DefaultFallback,
+        }
renamify-core/src/scanner.rs (1)

570-591: Potential UTF-8 panic when building line_after

line_string[match_col..] can panic if match_col isn’t on a char boundary. Use get() and fall back safely.

-        let line_after =
-            if match_col < line_string.len() && line_string[match_col..].starts_with(&content) {
-                let mut after_line = String::new();
-                after_line.push_str(&line_string[..match_col]);
-                after_line.push_str(&replace);
-                after_line.push_str(&line_string[match_col + content.len()..]);
-                after_line
-            } else {
+        let line_after = if let Some(tail) = line_string.get(match_col..) {
+            if tail.starts_with(&content) {
+                let mut after_line = String::new();
+                after_line.push_str(&line_string[..match_col]);
+                after_line.push_str(&replace);
+                after_line.push_str(&line_string[match_col + content.len()..]);
+                after_line
+            } else {
+                // Fallback: try to find the match in the line
+                if let Some(match_pos) = line_string.find(&content) {
+                    let mut after_line = String::new();
+                    after_line.push_str(&line_string[..match_pos]);
+                    after_line.push_str(&replace);
+                    after_line.push_str(&line_string[match_pos + content.len()..]);
+                    after_line
+                } else {
+                    line_before.clone()
+                }
+            }
+        } else {
             // Fallback: try to find the match in the line
             if let Some(match_pos) = line_string.find(&content) {
                 let mut after_line = String::new();
                 after_line.push_str(&line_string[..match_pos]);
                 after_line.push_str(&replace);
                 after_line.push_str(&line_string[match_pos + content.len()..]);
                 after_line
             } else {
                 // Could not find the match in the line - this shouldn't happen
                 line_before.clone()
             }
-            };
+        };
🧹 Nitpick comments (54)
renamify-cli/.gitignore (1)

1-2: Ignore entry looks good; consider adding project-wide ignore too

Nice addition. Consider also adding the same pattern to the repo root .gitignore so user workspaces don’t accidentally commit .renamify when running the CLI from project roots outside renamify-cli.

renamify-cli/src/cli/args.rs (2)

289-291: Flag naming may confuse with Apply’s --atomic

Rename/Plan use --atomic for “atomic identifiers,” while Apply uses --atomic for “atomic apply.” Consider a more explicit long flag (e.g., --atomic-identifiers) or clarifying help text to reduce ambiguity.

-        #[command(flatten)]
-        atomic: AtomicArgs,
+        #[command(flatten)]
+        atomic: AtomicArgs, // consider: rename long flag(s) to --atomic-identifiers/--no-atomic-identifiers

408-410: Parallels the Rename UX; consider Search parity

Plan exposes atomic flags; Search does not. If you want preview-only flows to reflect atomic tokenization, expose AtomicArgs on Search too for consistency.

 // In the Search subcommand fields:
-        #[command(flatten)]
-        acronyms: AcronymArgs,
+        #[command(flatten)]
+        acronyms: AcronymArgs,
+        #[command(flatten)]
+        atomic: AtomicArgs,
renamify-vscode/media/style.css (1)

146-229: Add keyboard accessibility for tooltips

Tooltips are hover-only. Add focus support so keyboard users can access them; also respect reduced motion.

 .tooltip-wrapper:hover .tooltip-text {
   opacity: 1;
   visibility: visible;
 }
+/* Keyboard accessibility */
+.tooltip-wrapper:focus-within .tooltip-text {
+  opacity: 1;
+  visibility: visible;
+}
+
+@media (prefers-reduced-motion: reduce) {
+  .tooltip-text {
+    transition: none;
+  }
+}

Also ensure the trigger element in the HTML is focusable (e.g., button with aria-describedby).

docs/src/content/docs/mcp/tools.mdx (4)

18-29: Document atomicSearch behavior relative to config.toml

Add a note that atomicSearch also auto-enables when the term matches an entry in .renamify/config.toml, and that an explicit false overrides config. This prevents confusion about precedence.


60-74: Clarify styles options parity with rename

Plan uses styles, whereas Rename uses includeStyles/excludeStyles/onlyStyles. Add a one-line note linking these so users know how to achieve the same filtering in each tool.


110-124: Atomic Mode example: add exact-match preservation note

Recommend adding a sentence that exact-casing of matched text is preserved when atomicSearch/atomicReplace are enabled (e.g., DocSpring → FormAPI, docspring → formapi).


384-395: Disambiguate “atomic” (apply) vs “atomic identifiers”

Elsewhere, “atomic” means transactional apply; here atomicSearch/atomicReplace refer to identifier indivisibility. Add an info callout to avoid conflation.

 ### Parameters
@@
-| `atomicSearch`  | boolean  | No       | `false`     | Treat search term as indivisible  |
-| `atomicReplace` | boolean  | No       | `false`     | Treat replace term as indivisible |
+| `atomicSearch`  | boolean  | No       | `false`     | Treat search term as indivisible  |
+| `atomicReplace` | boolean  | No       | `false`     | Treat replace term as indivisible |
+
+> Note: “atomicSearch/atomicReplace” (identifier indivisibility) is unrelated to the `atomic` flag in renamify_apply (transactional apply).
.taskmaster/docs/atomic-prd.txt (1)

33-35: Grammar: add “to”

“I want checkboxes to mark …”

- I want checkboxes to mark search/replace terms as atomic,
+ I want checkboxes to mark search/replace terms as atomic,
renamify-core/src/ambiguity/mod.rs (2)

49-51: Comment and implementation mismatch for Pascal

Comment says “has mixed case,” but implementation only checks starts uppercase and no separators. Align the comment to avoid confusion.

-            // PascalCase: starts uppercase, has mixed case, no separators
+            // PascalCase: starts uppercase, no separators (mixed case not required)

91-93: Title detection may admit “Title 123”; confirm numeric policy

If numerics in Title segments should be allowed, keep; otherwise consider restricting to letters and spaces.

renamify-core/src/lib.rs (1)

15-15: Consider re-exporting AtomicConfig and helpers for API parity

Most core modules re-export primary types/functions; do the same for atomic to simplify consumers.

 pub mod atomic;
+pub use atomic::{AtomicConfig /*, parse_atomic, to_atomic_style, generate_atomic_variants*/};
renamify-core/tests/compound_word_test.rs (1)

40-41: DRY up PlanOptions initialization (atomic_config default)

atomic_config: None is repeated in many tests. Introduce a small helper to reduce duplication and future drift.

+fn default_options() -> PlanOptions {
+    PlanOptions {
+        exclude_match: vec![],
+        exclude_matching_lines: None,
+        no_acronyms: false,
+        include_acronyms: vec![],
+        exclude_acronyms: vec![],
+        only_acronyms: vec![],
+        ignore_ambiguous: false,
+        atomic_config: None,
+        includes: vec![],
+        excludes: vec![],
+        respect_gitignore: false,
+        unrestricted_level: 0,
+        styles: None,
+        rename_files: false,
+        rename_dirs: false,
+        rename_root: false,
+        plan_out: PathBuf::from("plan.json"),
+        coerce_separators: renamify_core::scanner::CoercionMode::Auto,
+    }
+}

Optionally use struct update syntax in each test:

let mut options = default_options();
options.styles = Some(vec![renamify_core::Style::Pascal]);

Also applies to: 127-128, 210-211, 297-298, 369-370, 429-430, 511-512, 689-690, 789-790, 892-893, 1017-1018, 1141-1142, 1268-1269

renamify-cli/src/replace.rs (1)

144-152: Name collision risk: ApplyOptions.atomic vs “Atomic Identifiers”.

The boolean “atomic” here is about transactional apply, not Atomic Identifiers. Consider a brief comment to disambiguate for future readers.

   let apply_options = renamify_core::ApplyOptions {
     create_backups: true,
     backup_dir: PathBuf::from(".renamify/backups"),
-    atomic: true,
+    atomic: true, // transactional apply (not related to Atomic Identifiers)
     commit: false,
     force: force_with_conflicts,
     skip_symlinks: false,
     log_file: None,
   };
renamify-vscode/extension/src/cliService.ts (2)

432-434: Don’t skip version check when the search term is literally “version”.

args.includes('version') can match a positional search term. Check the subcommand instead.

-      if (!args.includes('version') && this.cliPath !== 'mocked-cli-path') {
+      if (args[0] !== 'version' && this.cliPath !== 'mocked-cli-path') {
         await this.checkVersionCompatibility();
       }

411-413: Return type nullability mismatch.

cliPath is string | null but getBinaryPath returns string | undefined. Coalesce to undefined (or change return type).

-  public getBinaryPath(): string | undefined {
-    return this.cliPath;
+  public getBinaryPath(): string | undefined {
+    return this.cliPath ?? undefined;
   }
renamify-cli/tests/cli_tests.rs (1)

98-101: Add a CLI end-to-end test for atomic mode
Include in renamify-cli/tests/cli_tests.rs a case that invokes the CLI with --atomic (or --atomic-search/--atomic-replace) and asserts atomic behavior (e.g. “GitHub → github”, not “git_hub”) to prevent regressions.

renamify-vscode/extension/templates/webview.hbs (1)

93-118: Improve accessibility for atomic tooltips.

Provide screen-reader context for the info icons.

-              <span class="codicon codicon-info tooltip-icon"></span>
+              <span class="codicon codicon-info tooltip-icon" aria-label="Atomic search info" role="img"></span>-              <span class="codicon codicon-info tooltip-icon"></span>
+              <span class="codicon codicon-info tooltip-icon" aria-label="Atomic replace info" role="img"></span>
renamify-core/tests/compound_position_test.rs (1)

36-37: Add at least one positive atomic-config test.

Cover an atomic scenario (e.g., search GitHub, replace github) with atomic_config=Some(...) to lock desired behavior.

renamify-core/tests/replace_column_position_test.rs (1)

25-26: Ensure column math stays correct under atomic mode.

Add a parallel case with atomic_config=Some(...) to assert that hunk.col remains 0-based and unchanged by atomic tokenization.

Also applies to: 92-93, 158-159

docs/src/content/docs/commands/plan.mdx (2)

61-69: Clarify atomic flag semantics and tighten wording

Suggest making precedence explicit and polishing phrasing for consistency.

Apply this diff:

-### Atomic Mode
+### Atomic Mode

-- `--atomic` - Treat both search and replace terms as indivisible units
-- `--atomic-search` - Only treat search term as atomic
-- `--atomic-replace` - Only treat replace term as atomic
-- `--no-atomic` - Disable atomic mode (override config)
-- `--no-atomic-search` - Disable atomic for search term
-- `--no-atomic-replace` - Disable atomic for replace term
+- `--atomic` - Treat both the search and replacement terms as indivisible units (overrides config)
+- `--atomic-search` - Treat only the search term as atomic (overrides config)
+- `--atomic-replace` - Treat only the replacement term as atomic (overrides config)
+- `--no-atomic` - Disable atomic mode (overrides config)
+- `--no-atomic-search` - Disable atomic for the search term (overrides config)
+- `--no-atomic-replace` - Disable atomic for the replacement term (overrides config)
+
+Note: If both enabling and disabling flags are provided, the corresponding `--no-atomic*` flag wins.

183-195: Improve example wording; “identifiers” vs “words” and add clarity

Minor wording to better reflect identifier semantics and set expectations.

Apply this diff:

-# Treat compound words as single units
+# Treat compound identifiers as single units
 renamify plan DocSpring FormAPI --atomic

-# Only search term is atomic
+# Only the search term is atomic (replacement will follow normal style rules)
 renamify plan DocSpring form_api --atomic-search

-# Only replace term is atomic
+# Only the replacement term is atomic (search will follow normal style rules)
 renamify plan doc_spring FormAPI --atomic-replace
renamify-core/tests/train_case_literals_test.rs (1)

175-175: Reduce boilerplate in test option setup (optional)

Consider a small helper/builder for PlanOptions in tests to avoid repeating unchanged defaults.

For example, add a tests/util.rs with a constructor that sets common defaults and accepts overrides.

docs/src/content/docs/commands/search.mdx (2)

57-63: Tighten Atomic Mode descriptions and consistency

Small grammar/consistency pass and explicit precedence.

Apply this diff:

-### Atomic Mode
+### Atomic Mode

-- `--atomic` - Treat the search term as an indivisible unit (no word boundaries)
-- `--atomic-search` - Same as `--atomic` for search command
-- `--no-atomic` - Disable atomic mode (override config)
-- `--no-atomic-search` - Same as `--no-atomic` for search command
+- `--atomic` - Treat the search term as an indivisible unit (no splitting across separators; overrides config)
+- `--atomic-search` - Same as `--atomic` for the search command (overrides config)
+- `--no-atomic` - Disable atomic mode (overrides config)
+- `--no-atomic-search` - Same as `--no-atomic` for the search command (overrides config)

142-149: Clarify what atomic search will not match

Spell out common near-misses to set expectations.

Apply this diff:

-# DocSpring will match as a whole word, not doc_spring
-renamify search DocSpring --atomic
+# DocSpring will match as a whole identifier (not "doc_spring" or "docspring")
+renamify search DocSpring --atomic
renamify-core/src/ambiguity/language_heuristics.rs (1)

16-26: Debug visibility for files without/invalid extensions

By computing extension with ?, we return early and emit no debug logs for extensionless or non-UTF-8 extensions. If you want visibility when debugging ambiguity, consider logging that case before returning.

Apply this diff:

-        let extension = file_path.extension()?.to_str()?;
-        let context = preceding_context.trim();
+        let context = preceding_context.trim();
+        let ext_opt = file_path.extension().and_then(|e| e.to_str());
+        let extension = match ext_opt {
+            Some(ext) => ext,
+            None => {
+                if std::env::var("RENAMIFY_DEBUG_AMBIGUITY").is_ok() {
+                    eprintln!("DEBUG LanguageHeuristics: unsupported or missing extension for {}", file_path.display());
+                    eprintln!("  preceding_context: {:?}", preceding_context);
+                    eprintln!("  context (trimmed): {:?}", context);
+                    eprintln!("  possible_styles: {:?}", possible_styles);
+                }
+                return None;
+            }
+        };
renamify-mcp/src/index.ts (1)

149-153: Atomic flag for search: consider string→boolean coercion

MCP callers sometimes send booleans as strings. Consider coercing to boolean to harden the schema.

-        atomicSearch: z
-          .boolean()
-          .optional()
+        atomicSearch: z.preprocess(
+          (v) => (v === 'true' ? true : v === 'false' ? false : v),
+          z.boolean()
+        ).optional()
           .describe('Treat search term as atomic (indivisible unit)'),
docs/src/content/docs/commands/rename.mdx (2)

70-77: Tighten phrasing for bullet list (minor grammar)

Reword for parallel construction and clarity.

- - `--atomic` - Treat both search and replace terms as indivisible units
- - `--atomic-search` - Only treat search term as atomic
- - `--atomic-replace` - Only treat replace term as atomic
- - `--no-atomic` - Disable atomic mode (override config)
- - `--no-atomic-search` - Disable atomic for search term
- - `--no-atomic-replace` - Disable atomic for replace term
+ - `--atomic` - Treat both search and replace terms as indivisible units
+ - `--atomic-search` - Treat only the search term as atomic
+ - `--atomic-replace` - Treat only the replace term as atomic
+ - `--no-atomic` - Disable atomic mode (overrides config)
+ - `--no-atomic-search` - Disable atomic mode for the search term
+ - `--no-atomic-replace` - Disable atomic mode for the replace term

126-135: Clarify replacement behavior in example

Add one line noting that atomic also affects how the replacement is rendered across styles.

 # With --atomic, DocSpring only matches docspring (as single unit)
+# And the replacement 'FormAPI' will render as 'formapi' in single-unit styles (no separators).
renamify-core/tests/multiple_matches_per_line_test.rs (2)

124-128: Dot-path test: make assertions deterministic and isolate plan file

Consider asserting the exact expected count (5) and writing plan_out into the temp dir to avoid cross-test collisions.

-    std::fs::write(
+    std::fs::write(
         &test_file,
         "mkdir .awesometool\n\
          cd .awesometool\n\
          echo 'awesometool' > .awesometool/config\n\
          rm -rf .awesometool\n",
     )
@@
-    let plan = scan_repository(&root, "awesometool", "smart_search", &options).unwrap();
+    let plan = scan_repository(&root, "awesometool", "smart_search", &options).unwrap();
@@
-    assert!(
-        plan.stats.total_matches >= 5,
-        "Should find awesometool in regular text and .awesometool paths"
-    );
+    assert_eq!(
+        plan.stats.total_matches, 5,
+        "Should find 5 occurrences (including dot-prefixed paths)"
+    );
@@
-    assert!(
-        !dot_matches.is_empty(),
-        "Should find .awesometool occurrences"
-    );
+    assert!(
+        dot_matches.len() >= 4,
+        "Should find .awesometool occurrences on mkdir/cd/path/rm lines"
+    );

And for all tests in this file, prefer:

-        plan_out: PathBuf::from("plan.json"),
+        plan_out: root.join("plan.json"),

Also applies to: 152-158, 160-174


18-37: Reduce PlanOptions boilerplate in tests

Introduce a small helper to build PlanOptions with sensible defaults, overridden per test, to cut duplication.

// tests/util.rs
pub fn base_options(root: &Path) -> PlanOptions {
    PlanOptions {
        exclude_match: vec![],
        exclude_matching_lines: None,
        no_acronyms: false,
        include_acronyms: vec![],
        exclude_acronyms: vec![],
        only_acronyms: vec![],
        ignore_ambiguous: false,
        atomic_config: None,
        includes: vec![],
        excludes: vec![],
        respect_gitignore: false,
        unrestricted_level: 0,
        styles: None,
        rename_files: false,
        rename_dirs: false,
        rename_root: false,
        plan_out: root.join("plan.json"),
        coerce_separators: renamify_core::scanner::CoercionMode::Auto,
    }
}
renamify-core/tests/dot_directory_test.rs (2)

43-45: Write plan.json inside temp dir

Avoid writing to CWD to prevent cross-test interference.

-        plan_out: PathBuf::from("plan.json"),
+        plan_out: root.join("plan.json"),

Also applies to: 140-142, 215-217, 268-270


47-60: Add debug logging of matched entries before asserting totals
To help diagnose occasional mismatches, print the full list of matches (e.g. println!("{:?}", plan.matches);) immediately before each assert_eq!(plan.stats.total_matches, …);.

renamify-mcp/src/renamify-service.ts (4)

29-30: Add brief docs for atomicSearch.

A short JSDoc clarifying semantics (tokenization boundary rules, default) will help tool consumers.

Apply this diff:

   ignoreAmbiguous?: boolean;
-  atomicSearch?: boolean;
+  /** Treat search term as an atomic identifier; only match whole atoms. */
+  atomicSearch?: boolean;

44-46: Annotate atomic flags for clarity and future compatibility.

Doc both options and note interaction with CLI flags.

   ignoreAmbiguous?: boolean;
-  atomicSearch?: boolean;
-  atomicReplace?: boolean;
+  /** Search side is atomic; narrows matches to whole identifiers. */
+  atomicSearch?: boolean;
+  /** Replacement side is atomic; preserves intended casing/boundaries. */
+  atomicReplace?: boolean;

254-265: Good helper extraction; consider reuse in rename for consistency.

Reuse these helpers in rename() to avoid flag drift.

   private addAtomicReplaceArg(args: string[], atomicReplace?: boolean): void {
     if (atomicReplace) {
       args.push('--atomic-replace');
     }
   }

485-492: Deduplicate flag assembly in rename().

Use the same helpers to keep behavior aligned with plan/search.

-    // Add atomic flags
-    if (options.atomicSearch) {
-      args.push('--atomic-search');
-    }
-    if (options.atomicReplace) {
-      args.push('--atomic-replace');
-    }
+    // Add atomic flags
+    this.addAtomicSearchArg(args, options.atomicSearch);
+    this.addAtomicReplaceArg(args, options.atomicReplace);
renamify-core/tests/coercion_tests.rs (1)

119-120: Explicit atomic_config: None in tests is appropriate.

Keeps legacy coercion behavior stable while the atomic feature is introduced. Consider a small helper to construct PlanOptions with common defaults to reduce duplication.

Also applies to: 219-220, 285-286, 384-385, 485-486, 546-547, 622-623

renamify-core/src/config.rs (2)

11-14: Config surface addition looks good; confirm case-sensitivity expectations.

If matching is case-sensitive (DocSpring vs docspring), mention it in the doc comment for fewer surprises.


152-167: Solid parsing test for atomic identifiers.

Optional: add a round-trip save/load test and assert default is empty in test_default_config.

renamify-cli/src/plan.rs (1)

56-66: AtomicConfig construction logic reads well; minor simplification possible.

from_flags_and_config already treats atomic_both as authoritative; computing the ORs is fine but slightly redundant.

renamify-vscode/webview/src/webview.ts (1)

36-47: Null-safety for optional checkboxes.

Guard against template mismatches to avoid runtime errors and default to false in payload/state.

-  const renamePathsCheckbox = document.getElementById(
-    'renamePathsCheckbox'
-  ) as HTMLInputElement;
+  const renamePathsCheckbox = document.getElementById(
+    'renamePathsCheckbox'
+  ) as HTMLInputElement | null;
-  const ignoreAmbiguousCheckbox = document.getElementById(
-    'ignoreAmbiguousCheckbox'
-  ) as HTMLInputElement;
+  const ignoreAmbiguousCheckbox = document.getElementById(
+    'ignoreAmbiguousCheckbox'
+  ) as HTMLInputElement | null;
-  const atomicSearchCheckbox = document.getElementById(
-    'atomicSearchCheckbox'
-  ) as HTMLInputElement;
+  const atomicSearchCheckbox = document.getElementById(
+    'atomicSearchCheckbox'
+  ) as HTMLInputElement | null;
-  const atomicReplaceCheckbox = document.getElementById(
-    'atomicReplaceCheckbox'
-  ) as HTMLInputElement;
+  const atomicReplaceCheckbox = document.getElementById(
+    'atomicReplaceCheckbox'
+  ) as HTMLInputElement | null;
-  renamePathsCheckbox.addEventListener('change', () => {
+  renamePathsCheckbox?.addEventListener('change', () => {
     performSearch();
     saveState();
   });
-  ignoreAmbiguousCheckbox.addEventListener('change', () => {
+  ignoreAmbiguousCheckbox?.addEventListener('change', () => {
     performSearch();
     saveState();
   });
-  atomicSearchCheckbox.addEventListener('change', () => {
+  atomicSearchCheckbox?.addEventListener('change', () => {
     performSearch();
     saveState();
   });
-  atomicReplaceCheckbox.addEventListener('change', () => {
+  atomicReplaceCheckbox?.addEventListener('change', () => {
     performSearch();
     saveState();
   });
-      renamePaths: renamePathsCheckbox.checked,
-      ignoreAmbiguous: ignoreAmbiguousCheckbox.checked,
-      atomicSearch: atomicSearchCheckbox.checked,
-      atomicReplace: atomicReplaceCheckbox.checked,
+      renamePaths: renamePathsCheckbox?.checked ?? false,
+      ignoreAmbiguous: ignoreAmbiguousCheckbox?.checked ?? false,
+      atomicSearch: atomicSearchCheckbox?.checked ?? false,
+      atomicReplace: atomicReplaceCheckbox?.checked ?? false,
-      renamePaths: renamePathsCheckbox.checked,
-      ignoreAmbiguous: ignoreAmbiguousCheckbox.checked,
-      atomicSearch: atomicSearchCheckbox.checked,
-      atomicReplace: atomicReplaceCheckbox.checked,
+      renamePaths: renamePathsCheckbox?.checked ?? false,
+      ignoreAmbiguous: ignoreAmbiguousCheckbox?.checked ?? false,
+      atomicSearch: atomicSearchCheckbox?.checked ?? false,
+      atomicReplace: atomicReplaceCheckbox?.checked ?? false,
-    if (state.renamePaths !== undefined) {
-      renamePathsCheckbox.checked = state.renamePaths;
-    }
+    if (state.renamePaths !== undefined && renamePathsCheckbox) {
+      renamePathsCheckbox.checked = state.renamePaths;
+    }
-    if (state.ignoreAmbiguous !== undefined) {
-      ignoreAmbiguousCheckbox.checked = state.ignoreAmbiguous;
-    }
+    if (state.ignoreAmbiguous !== undefined && ignoreAmbiguousCheckbox) {
+      ignoreAmbiguousCheckbox.checked = state.ignoreAmbiguous;
+    }
-    if (state.atomicSearch !== undefined) {
-      atomicSearchCheckbox.checked = state.atomicSearch;
-    }
+    if (state.atomicSearch !== undefined && atomicSearchCheckbox) {
+      atomicSearchCheckbox.checked = state.atomicSearch;
+    }
-    if (state.atomicReplace !== undefined) {
-      atomicReplaceCheckbox.checked = state.atomicReplace;
-    }
+    if (state.atomicReplace !== undefined && atomicReplaceCheckbox) {
+      atomicReplaceCheckbox.checked = state.atomicReplace;
+    }
-      renamePaths: renamePathsCheckbox.checked,
-      ignoreAmbiguous: ignoreAmbiguousCheckbox.checked,
-      atomicSearch: atomicSearchCheckbox.checked,
-      atomicReplace: atomicReplaceCheckbox.checked,
+      renamePaths: renamePathsCheckbox?.checked ?? false,
+      ignoreAmbiguous: ignoreAmbiguousCheckbox?.checked ?? false,
+      atomicSearch: atomicSearchCheckbox?.checked ?? false,
+      atomicReplace: atomicReplaceCheckbox?.checked ?? false,

Also applies to: 182-199, 264-268, 295-299, 809-822, 839-845

renamify-core/tests/format_string_test.rs (1)

52-54: Fix misleading parameter comments (these are acronym lists, not atomic flags).

These three Vec params are include_acronyms, exclude_acronyms, only_acronyms. The current comments suggest atomic flags, which is incorrect and confusing.

Apply this diff:

-        vec![], // custom_acronyms
-        vec![], // atomic_search
-        vec![], // atomic_replace
+        vec![], // include_acronyms
+        vec![], // exclude_acronyms
+        vec![], // only_acronyms
renamify-core/tests/exact_match_preserve_test.rs (1)

1285-1296: Avoid mentioning Dot-case here unless explicitly included in styles.

Default styles exclude Dot, so referencing "doc.spring" in the allowed set is slightly misleading. Consider dropping it or adding Dot to the styles for this test.

Apply this diff (option A: drop Dot-case from the assertion text and allowed set):

-        assert!(
-            formapi_value == "doc_spring"
-                || formapi_value == "doc-spring"
-                || formapi_value == "doc.spring"
-        );
+        assert!(
+            formapi_value == "doc_spring"
+                || formapi_value == "doc-spring"
+        );

Option B: include Dot explicitly in styles when building the map.

renamify-core/src/case_model.rs (1)

600-609: De-duplicate the default styles source.

You already expose Style::default_styles(). Duplicating the list here risks drift. Build a Vec once and iterate over it.

Apply this diff:

-    let default_styles = [
-        Style::Snake,
-        Style::Kebab,
-        Style::Camel,
-        Style::Pascal,
-        Style::ScreamingSnake,
-        Style::Train, // Include Train-Case for patterns like Renamify-Core-Engine
-        Style::ScreamingTrain, // Include ScreamingTrain for patterns like RENAMIFY-DEBUG
-    ];
-    let styles = styles.unwrap_or(&default_styles);
+    let default_styles_vec = Style::default_styles();
+    let styles: &[Style] = styles.unwrap_or(&default_styles_vec);
renamify-core/tests/ambiguity_integration_test.rs (1)

1-68: General: good coverage; consider printing matches only on failure.

The eprintln! loop is noisy in passing runs. Wrap in a test-only debug flag or print on assertion failure.

renamify-core/src/atomic.rs (3)

6-16: TS export of HashSet may not map cleanly to TypeScript

ts-rs support for HashSet is not universal. To avoid TS type-generation issues, annotate or switch to Vec for the wire type.

Apply one of:

 pub struct AtomicConfig {
-    /// Set of identifiers that should be treated as atomic (indivisible)
-    pub atomic_identifiers: HashSet<String>,
+    /// Set of identifiers that should be treated as atomic (indivisible)
+    #[ts(type = "Array<string>")]
+    pub atomic_identifiers: HashSet<String>,

or:

-    pub atomic_identifiers: HashSet<String>,
+    pub atomic_identifiers: Vec<String>,

If you switch to Vec, normalize into a HashSet internally in from_flags_and_config.


23-33: Case-insensitive match is ASCII-only

eq_ignore_ascii_case() skips non-ASCII. If users add non-ASCII identifiers (e.g., “München”), comparisons will fail.

-            .any(|atomic| atomic.eq_ignore_ascii_case(identifier))
+            .any(|atomic| atomic.to_lowercase() == identifier.to_lowercase())

This is slower but correct. If perf matters, consider unicode casefolding (unicode-normalization).


78-109: Camel-case lowering is fine; consider caching for repeated calls

to_atomic_style allocates per call. If used in tight loops (variant gen), consider small caching when styles.len() is large. Not urgent.

renamify-core/src/ambiguity/resolver.rs (1)

80-87: Reduce repeated env var checks for debug logging

Cache the debug flag once.

-        if std::env::var("RENAMIFY_DEBUG_AMBIGUITY").is_ok() {
+        let debug = std::env::var("RENAMIFY_DEBUG_AMBIGUITY").is_ok();
+        if debug {
             eprintln!(
                 "=== Resolving ambiguity for '{}' -> '{}' ===",
                 matched_text, replacement_text
             );
-            eprintln!("  File: {:?}", context.file_path);
-            eprintln!("  Content: {:?}", context.line_content);
+            eprintln!("  File: {:?}", context.file_path);
+            eprintln!("  Content: {:?}", context.line_content);
         }
...
-                if std::env::var("RENAMIFY_DEBUG_AMBIGUITY").is_ok() {
+                if debug {
                     eprintln!("  -> Not ambiguous, detected style: {:?}", style);
                 }

Propagate debug where used.

Also applies to: 92-95, 114-116, 122-124, 130-133

renamify-core/src/scanner.rs (4)

62-64: TS/serde surface for AtomicConfig

PlanOptions exposes atomic_config over TS. Given AtomicConfig.atomic_identifiers is a HashSet, ensure ts-rs can generate correct TS. If not, add #[ts(type = "...")] on the AtomicConfig field or switch to Vec as suggested in atomic.rs.

Also applies to: 101-102


457-462: Avoid re-tokenizing replacement on every ambiguous match

Cache tokens once per file.

-    // Calculate possible styles for the replacement text ONCE for all matches in this file
-    let replacement_possible_styles = get_possible_styles(original_replacement);
+    // Calculate once per file
+    let replacement_possible_styles = get_possible_styles(original_replacement);
+    let replacement_tokens_cache = parse_to_tokens(original_replacement);
...
-            let replacement_tokens = parse_to_tokens(original_replacement);
-            let styled_replacement = to_style(&replacement_tokens, resolved.style);
+            let styled_replacement = to_style(&replacement_tokens_cache, resolved.style);

Also applies to: 520-523


381-389: Plan.version should be consistent with create_simple_plan

scan_repository_multi hardcodes "1.0.0" while create_simple_plan uses env!("CARGO_PKG_VERSION"). Prefer the latter for consistency.

-        version: "1.0.0".to_string(),
+        version: env!("CARGO_PKG_VERSION").to_string(),

405-424: Directory glob heuristic may overmatch single-file names

Heuristic treats patterns without wildcards and without dots as directories (e.g., "LICENSE"). This can unintentionally expand to "LICENSE/**".

Consider checking filesystem when roots are known, or require a trailing slash to opt-in to recursive dir match.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c6d1e45 and 79a8e21.

📒 Files selected for processing (51)
  • .taskmaster/docs/atomic-prd.txt (1 hunks)
  • CLAUDE.md (1 hunks)
  • docs/astro.config.mjs (1 hunks)
  • docs/src/content/docs/commands/plan.mdx (2 hunks)
  • docs/src/content/docs/commands/rename.mdx (2 hunks)
  • docs/src/content/docs/commands/search.mdx (2 hunks)
  • docs/src/content/docs/features/atomic-identifiers.mdx (1 hunks)
  • docs/src/content/docs/mcp/tools.mdx (5 hunks)
  • renamify-cli/.gitignore (1 hunks)
  • renamify-cli/src/cli/args.rs (3 hunks)
  • renamify-cli/src/main.rs (4 hunks)
  • renamify-cli/src/plan.rs (4 hunks)
  • renamify-cli/src/replace.rs (1 hunks)
  • renamify-cli/tests/binary_name_preservation.rs (1 hunks)
  • renamify-cli/tests/cli_tests.rs (3 hunks)
  • renamify-core/src/ambiguity/language_heuristics.rs (1 hunks)
  • renamify-core/src/ambiguity/mod.rs (3 hunks)
  • renamify-core/src/ambiguity/resolver.rs (6 hunks)
  • renamify-core/src/atomic.rs (1 hunks)
  • renamify-core/src/case_model.rs (9 hunks)
  • renamify-core/src/config.rs (2 hunks)
  • renamify-core/src/lib.rs (1 hunks)
  • renamify-core/src/operations/plan.rs (2 hunks)
  • renamify-core/src/operations/rename.rs (1 hunks)
  • renamify-core/src/scanner.rs (10 hunks)
  • renamify-core/tests/acronym_cli_test.rs (5 hunks)
  • renamify-core/tests/acronym_variant_test.rs (5 hunks)
  • renamify-core/tests/ambiguity_integration_test.rs (1 hunks)
  • renamify-core/tests/coercion_separator_test.rs (11 hunks)
  • renamify-core/tests/coercion_tests.rs (7 hunks)
  • renamify-core/tests/compound_position_test.rs (4 hunks)
  • renamify-core/tests/compound_word_test.rs (13 hunks)
  • renamify-core/tests/diff_rendering_test.rs (2 hunks)
  • renamify-core/tests/dot_directory_test.rs (4 hunks)
  • renamify-core/tests/exact_match_preserve_test.rs (1 hunks)
  • renamify-core/tests/format_string_test.rs (1 hunks)
  • renamify-core/tests/hyphenated_capitalized_test.rs (5 hunks)
  • renamify-core/tests/mixed_style_test.rs (3 hunks)
  • renamify-core/tests/multiple_matches_per_line_test.rs (9 hunks)
  • renamify-core/tests/replace_column_position_test.rs (3 hunks)
  • renamify-core/tests/replace_include_flag_test.rs (3 hunks)
  • renamify-core/tests/tokenization_edge_cases_test.rs (8 hunks)
  • renamify-core/tests/train_case_apply_test.rs (1 hunks)
  • renamify-core/tests/train_case_literals_test.rs (2 hunks)
  • renamify-mcp/src/index.ts (3 hunks)
  • renamify-mcp/src/renamify-service.ts (7 hunks)
  • renamify-vscode/extension/src/cliService.ts (3 hunks)
  • renamify-vscode/extension/src/types.ts (4 hunks)
  • renamify-vscode/extension/templates/webview.hbs (1 hunks)
  • renamify-vscode/media/style.css (1 hunks)
  • renamify-vscode/webview/src/webview.ts (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
renamify-core/tests/format_string_test.rs (1)
renamify-core/src/operations/plan.rs (1)
  • plan_operation (10-144)
renamify-core/tests/exact_match_preserve_test.rs (3)
renamify-core/src/case_model.rs (2)
  • generate_variant_map (585-591)
  • generate_variant_map_with_atomic (593-686)
renamify-core/src/scanner.rs (1)
  • get (802-819)
renamify-core/src/atomic.rs (1)
  • from_flags_and_config (36-59)
renamify-core/tests/ambiguity_integration_test.rs (1)
renamify-core/src/operations/plan.rs (1)
  • plan_operation (10-144)
renamify-core/src/atomic.rs (2)
renamify-core/src/scanner.rs (2)
  • new (785-787)
  • default (82-103)
renamify-core/src/case_model.rs (3)
  • new (43-45)
  • new (54-56)
  • first (581-581)
renamify-core/src/scanner.rs (2)
renamify-core/src/ambiguity/mod.rs (2)
  • get_possible_styles (114-136)
  • is_ambiguous (139-141)
renamify-core/src/case_model.rs (5)
  • parse_to_tokens (186-188)
  • to_style (450-567)
  • new (43-45)
  • new (54-56)
  • generate_variant_map_with_atomic (593-686)
renamify-core/src/case_model.rs (3)
renamify-core/src/atomic.rs (4)
  • new (19-21)
  • parse_atomic (73-76)
  • to_atomic_style (79-110)
  • from_flags_and_config (36-59)
renamify-core/src/acronym.rs (2)
  • new (52-58)
  • get_default_acronym_set (17-19)
renamify-core/src/ambiguity/mod.rs (1)
  • is_ambiguous (139-141)
renamify-core/src/ambiguity/resolver.rs (2)
renamify-core/src/ambiguity/mod.rs (2)
  • is_ambiguous (139-141)
  • get_possible_styles (114-136)
renamify-core/src/case_model.rs (1)
  • detect_style (59-154)
renamify-core/tests/multiple_matches_per_line_test.rs (1)
renamify-core/src/scanner.rs (1)
  • scan_repository (204-206)
renamify-cli/src/plan.rs (2)
renamify-core/src/config.rs (1)
  • load (61-71)
renamify-core/src/atomic.rs (1)
  • from_flags_and_config (36-59)
🪛 LanguageTool
.taskmaster/docs/atomic-prd.txt

[grammar] ~3-~3: There might be a mistake here.
Context: ...uirements Document ## Executive Summary Add support for treating certain identif...

(QB_NEW_EN)


[grammar] ~6-~6: There might be a mistake here.
Context: ...n all case styles. ## Problem Statement Currently, when renaming identifiers lik...

(QB_NEW_EN)


[grammar] ~22-~22: There might be a mistake here.
Context: ...tory 1: CLI User with Atomic Identifiers As a developer renaming "HubGit" to "Git...

(QB_NEW_EN)


[grammar] ~23-~23: There might be a mistake here.
Context: ...ing "HubGit" to "GitHub" in my codebase, I want to specify that both names are at...

(QB_NEW_EN)


[grammar] ~27-~27: There might be a mistake here.
Context: ...ect with Persistent Atomic Configuration As a team maintaining a project with bra...

(QB_NEW_EN)


[style] ~29-~29: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ct with brand names like "DocSpring", I want to configure these as atomic in a config f...

(REP_WANT_TO_VB)


[grammar] ~32-~32: There might be a mistake here.
Context: ...or. ### Story 3: VS Code Extension User As a VS Code user, I want checkboxes to ...

(QB_NEW_EN)


[grammar] ~33-~33: There might be a mistake here.
Context: ...S Code Extension User As a VS Code user, I want checkboxes to mark search/replace...

(QB_NEW_EN)


[grammar] ~37-~37: There might be a mistake here.
Context: ...y. ### Story 4: Partial Atomic Matching As a developer with "FormAPIController" ...

(QB_NEW_EN)


[grammar] ~38-~38: There might be a mistake here.
Context: ...per with "FormAPIController" in my code, I want atomic "FormAPI" to match and rep...

(QB_NEW_EN)


[grammar] ~44-~44: There might be a mistake here.
Context: ...quirements ### 1. Core Atomic Detection - When an identifier is marked as atomic, ...

(QB_NEW_EN)


[grammar] ~51-~51: There might be a mistake here.
Context: ...eplace are atomic - --atomic-search - Only search term is atomic - `--atomic-repla...

(QB_NEW_EN)


[style] ~52-~52: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ...h term is atomic - --atomic-replace - Only replace term is atomic - --no-atomic ...

(ADVERB_REPETITION_PREMIUM)


[grammar] ~54-~54: There might be a mistake here.
Context: ... --no-atomic-search - Override config for search term - --no-atomic-replace - O...

(QB_NEW_EN)


[grammar] ~55-~55: There might be a mistake here.
Context: ...--no-atomic-replace - Override config for replace term - Error if user specifies ...

(QB_NEW_EN)


[grammar] ~56-~56: There might be a mistake here.
Context: ...verride config for replace term - Error if user specifies both --atomic and `--a...

(QB_NEW_EN)


[grammar] ~58-~58: There might be a mistake here.
Context: ...omic-replace ### 3. Configuration File - Supportatomic = ["DocSpring", "GitHub"...

(QB_NEW_EN)


[grammar] ~59-~59: There might be a mistake here.
Context: ...= ["DocSpring", "GitHub", "GitHub"]in.renamify/config.toml` - Config entries are case-insensitive matc...

(QB_NEW_EN)


[grammar] ~64-~64: There might be a mistake here.
Context: ...nfig settings ### 4. Variant Generation For atomic identifier "DocSpring": - Gen...

(QB_NEW_EN)


[grammar] ~65-~65: There might be a mistake here.
Context: ...ation For atomic identifier "DocSpring": - Generate only: DocSpring, docspring,...

(QB_NEW_EN)


[grammar] ~69-~69: There might be a mistake here.
Context: ...ng ### 5. Compound Identifier Handling When "FormAPI" is atomic: -FormAPICont...

(QB_NEW_EN)


[grammar] ~76-~76: There might be a mistake here.
Context: ...er identifiers ### 6. VS Code Extension - Add "Atomic" checkbox under Search field...

(QB_NEW_EN)


[grammar] ~82-~82: There might be a mistake here.
Context: ... match config entries ### 7. MCP Server - Add atomicSearch: boolean parameter - ...

(QB_NEW_EN)


[grammar] ~89-~89: There might be a mistake here.
Context: ...uirements ### 1. Case Model Refactoring - Split case_model.rs into smaller modul...

(QB_NEW_EN)


[grammar] ~94-~94: There might be a mistake here.
Context: ...d compatibility ### 2. Token Generation - Modify parse_to_tokens to accept atomi...

(QB_NEW_EN)


[grammar] ~96-~96: There might be a mistake here.
Context: ...` to accept atomic hints - When atomic, return single token regardless of internal cap...

(QB_NEW_EN)


[grammar] ~99-~99: There might be a mistake here.
Context: ...ot atomic ### 3. Variant Map Generation - Update generate_variant_map to accept ...

(QB_NEW_EN)


[grammar] ~104-~104: There might be a mistake here.
Context: ...mic/non-atomic scenarios ### 4. Testing - Unit tests for atomic token parsing - In...

(QB_NEW_EN)


[grammar] ~105-~105: There might be a mistake here.
Context: ...ng - Unit tests for atomic token parsing - Integration tests for compound identifie...

(QB_NEW_EN)


[grammar] ~106-~106: There might be a mistake here.
Context: ...tegration tests for compound identifiers - End-to-end tests with real examples (Doc...

(QB_NEW_EN)


[grammar] ~107-~107: There might be a mistake here.
Context: ...s with real examples (DocSpring, GitHub) - Performance tests to ensure no regressio...

(QB_NEW_EN)


[grammar] ~112-~112: There might be a mistake here.
Context: ...ctional Requirements ### 1. Performance - Atomic detection should not significantl...

(QB_NEW_EN)


[grammar] ~117-~117: There might be a mistake here.
Context: ...tion - Create "Atomic Identifiers" page in Features section - Include examples of ...

(QB_NEW_EN)


[grammar] ~119-~119: There might be a mistake here.
Context: ...e examples of when to use atomic mode - Document config file format ### 3. Error Handli...

(QB_NEW_EN)


[grammar] ~121-~121: There might be a mistake here.
Context: ...onfig file format ### 3. Error Handling - Clear error messages for conflicting fla...

(QB_NEW_EN)


[grammar] ~164-~164: There might be a mistake here.
Context: ...Plan ### Phase 1: Core Support (Week 1) 1. Refactor case_model.rs into modules 2. I...

(QB_NEW_EN)


[grammar] ~170-~170: There might be a mistake here.
Context: ...s ### Phase 2: CLI Integration (Week 1) 1. Add CLI flags 2. Implement config file s...

(QB_NEW_EN)


[grammar] ~176-~176: There might be a mistake here.
Context: ...ts ### Phase 3: UI Integration (Week 2) 1. Update VS Code extension 2. Update MCP s...

(QB_NEW_EN)


[grammar] ~182-~182: There might be a mistake here.
Context: ...ing ### Phase 4: Documentation (Week 2) 1. Create atomic identifiers documentation ...

(QB_NEW_EN)


[grammar] ~187-~187: There might be a mistake here.
Context: ...README and help text ## Success Metrics - All test cases pass - No performance reg...

(QB_NEW_EN)


[grammar] ~195-~195: There might be a mistake here.
Context: ...itigations ### Risk 1: Breaking Changes Mitigation: Atomic mode is opt-in, exi...

(QB_NEW_EN)


[grammar] ~198-~198: There might be a mistake here.
Context: ... default ### Risk 2: Performance Impact Mitigation: Early detection during par...

(QB_NEW_EN)


[grammar] ~201-~201: There might be a mistake here.
Context: ...before/after ### Risk 3: User Confusion Mitigation: Clear documentation, intui...

(QB_NEW_EN)

docs/src/content/docs/commands/rename.mdx

[grammar] ~73-~73: There might be a mistake here.
Context: ...isible units - --atomic-search - Only treat search term as atomic - `--atomic-repla...

(QB_NEW_EN)


[grammar] ~74-~74: There might be a mistake here.
Context: ...m as atomic - --atomic-replace - Only treat replace term as atomic - --no-atomic ...

(QB_NEW_EN)


[grammar] ~76-~76: There might be a mistake here.
Context: ...- --no-atomic-search - Disable atomic for search term - --no-atomic-replace - D...

(QB_NEW_EN)


[grammar] ~77-~77: There might be a mistake here.
Context: ... --no-atomic-replace - Disable atomic for replace term ### Display Options - `-...

(QB_NEW_EN)

docs/src/content/docs/commands/plan.mdx

[grammar] ~64-~64: There might be a mistake here.
Context: ...isible units - --atomic-search - Only treat search term as atomic - `--atomic-repla...

(QB_NEW_EN)


[grammar] ~65-~65: There might be a mistake here.
Context: ...m as atomic - --atomic-replace - Only treat replace term as atomic - --no-atomic ...

(QB_NEW_EN)


[grammar] ~67-~67: There might be a mistake here.
Context: ...- --no-atomic-search - Disable atomic for search term - --no-atomic-replace - D...

(QB_NEW_EN)


[grammar] ~68-~68: There might be a mistake here.
Context: ... --no-atomic-replace - Disable atomic for replace term ### Display Options - `-...

(QB_NEW_EN)

docs/src/content/docs/features/atomic-identifiers.mdx

[grammar] ~125-~125: There might be a mistake here.
Context: ...yle | Non-Atomic | Atomic | | -------------------- | ---------- | --...

(QB_NEW_EN)


[grammar] ~126-~126: There might be a mistake here.
Context: ...------------- | ---------- | --------- | | snake_case | doc_spring | do...

(QB_NEW_EN)


[grammar] ~127-~127: There might be a mistake here.
Context: ...ase | doc_spring | docspring | | kebab-case | doc-spring | do...

(QB_NEW_EN)


[grammar] ~128-~128: There might be a mistake here.
Context: ...ase | doc-spring | docspring | | camelCase | docSpring | do...

(QB_NEW_EN)


[grammar] ~129-~129: There might be a mistake here.
Context: ...se | docSpring | docspring | | PascalCase | DocSpring | Do...

(QB_NEW_EN)


[grammar] ~130-~130: There might be a mistake here.
Context: ...ase | DocSpring | DocSpring | | SCREAMING_SNAKE_CASE | DOC_SPRING | DO...

(QB_NEW_EN)


[grammar] ~131-~131: There might be a mistake here.
Context: ...NG_SNAKE_CASE | DOC_SPRING | DOCSPRING | | Train-Case | Doc-Spring | Do...

(QB_NEW_EN)


[grammar] ~132-~132: There might be a mistake here.
Context: ...ase | Doc-Spring | Docspring | | lowercase | docspring | do...

(QB_NEW_EN)


[grammar] ~133-~133: There might be a mistake here.
Context: ...se | docspring | docspring | | UPPERCASE | DOCSPRING | DO...

(QB_NEW_EN)


[grammar] ~147-~147: There might be a mistake here.
Context: ...Add frequently used brand names to your .renamify/config.toml 2. Use for Compound Words: Apply atomic m...

(QB_NEW_EN)


[grammar] ~166-~166: There might be a mistake here.
Context: ...ations required - Config file is cached after first read ## Limitations - Atomic mo...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Test on ubuntu-latest
  • GitHub Check: Test VS Code Extension (ubuntu-latest, 20.x)
  • GitHub Check: Test VS Code Extension (macos-latest, 20.x)
  • GitHub Check: Test VS Code Extension (windows-latest, 20.x)
  • GitHub Check: Test on macos-latest
  • GitHub Check: Test on windows-latest
  • GitHub Check: Self-Hosting Demo (ubuntu-latest)
  • GitHub Check: Self-Hosting Demo (windows-latest)
  • GitHub Check: Self-Hosting Demo (macos-latest)

Comment on lines +59 to +61
- Support `atomic = ["DocSpring", "GitHub", "GitHub"]` in `.renamify/config.toml`
- Config entries are case-insensitive matched (PascalCase in config matches all cases)
- Config auto-applies when matching identifiers are used
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove duplicate “GitHub” in config example

Deduplicate to avoid implying duplicates are meaningful.

- - Support `atomic = ["DocSpring", "GitHub", "GitHub"]` in `.renamify/config.toml`
+ - Support `atomic = ["DocSpring", "GitHub"]` in `.renamify/config.toml`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Support `atomic = ["DocSpring", "GitHub", "GitHub"]` in `.renamify/config.toml`
- Config entries are case-insensitive matched (PascalCase in config matches all cases)
- Config auto-applies when matching identifiers are used
- Support `atomic = ["DocSpring", "GitHub"]` in `.renamify/config.toml`
- Config entries are case-insensitive matched (PascalCase in config matches all cases)
- Config auto-applies when matching identifiers are used
🧰 Tools
🪛 LanguageTool

[grammar] ~59-~59: There might be a mistake here.
Context: ...= ["DocSpring", "GitHub", "GitHub"]in.renamify/config.toml` - Config entries are case-insensitive matc...

(QB_NEW_EN)

🤖 Prompt for AI Agents
.taskmaster/docs/atomic-prd.txt around lines 59-61: the example list for atomic
in `.renamify/config.toml` contains a duplicate "GitHub"; remove the duplicate
so each provider appears only once (e.g., `atomic = ["DocSpring", "GitHub"]`)
and update any surrounding text if it refers to duplicates so the example no
longer implies duplicates are meaningful.

Comment on lines +131 to +134
- `git_lab_api` → `GitHub_api`
- `GIT_LAB_TOKEN` → `GitHub_TOKEN`
- `GitLabController` → `GitHubController`

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect acceptance examples for atomic mapping

Atomic GitLab → GitHub should map styles consistently:

  • git_lab_api → github_api
  • GIT_LAB_TOKEN → GITHUB_TOKEN

Current examples mix cases (GitHub_api, GitHub_TOKEN).

- - `git_lab_api` → `GitHub_api`
- - `GIT_LAB_TOKEN` → `GitHub_TOKEN`
+ - `git_lab_api` → `github_api`
+ - `GIT_LAB_TOKEN` → `GITHUB_TOKEN`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `git_lab_api` → `GitHub_api`
- `GIT_LAB_TOKEN` → `GitHub_TOKEN`
- `GitLabController` → `GitHubController`
- `git_lab_api` → `github_api`
- `GIT_LAB_TOKEN` → `GITHUB_TOKEN`
- `GitLabController` → `GitHubController`
🤖 Prompt for AI Agents
In .taskmaster/docs/atomic-prd.txt around lines 131 to 134, the acceptance
examples for the atomic GitLab→GitHub mapping use inconsistent casing (e.g.,
GitHub_api, GitHub_TOKEN); update the examples so they consistently use
lowercase/hyphenation as specified: replace `GitHub_api` with `github_api` and
`GitHub_TOKEN` with `GITHUB_TOKEN` (and likewise ensure `GitLabController`
becomes `GitHubController` only if controller naming should follow the same
casing rules), so all examples consistently reflect `git_lab_api` → `github_api`
and `GIT_LAB_TOKEN` → `GITHUB_TOKEN`.

Comment on lines +139 to +143
`.renamify/config.toml`

- Project configuration including atomic identifiers
- `atomic = ["DocSpring", "GitHub", "GitHub"]` - identifiers treated as indivisible units

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Duplicate identifier in example atomic list

"GitHub" appears twice. Deduplicate to avoid confusing users configuring atomic identifiers.

-- `atomic = ["DocSpring", "GitHub", "GitHub"]` - identifiers treated as indivisible units
+- `atomic = ["DocSpring", "GitHub"]` - identifiers treated as indivisible units
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`.renamify/config.toml`
- Project configuration including atomic identifiers
- `atomic = ["DocSpring", "GitHub", "GitHub"]` - identifiers treated as indivisible units
`.renamify/config.toml`
- Project configuration including atomic identifiers
- `atomic = ["DocSpring", "GitHub"]` - identifiers treated as indivisible units
🤖 Prompt for AI Agents
In CLAUDE.md around lines 139 to 143, the example atomic list contains a
duplicate "GitHub" entry; update the example to remove the duplicate so each
identifier appears only once (e.g., change atomic = ["DocSpring", "GitHub"]) and
ensure the documentation text reflects that atomic identifiers should be unique
to avoid user confusion.

Comment on lines +47 to +66
<Tabs>
<TabItem label="Both atomic">
```bash # Both search and replace terms are atomic renamify rename FormAPI
DocSpring --atomic
```
</TabItem>
<TabItem label="Search only">
```bash # Only search term is atomic renamify rename FormAPI Helper
--atomic-search
```
</TabItem>
<TabItem label="Replace only">

```bash # Only replace term is atomic renamify rename OldName DocSpring
--atomic-replace
```

</TabItem>
</Tabs>

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix broken CLI examples in tabs.

Code blocks mix comments and incomplete commands; render incorrectly. Replace with valid commands.

 <Tabs>
 <TabItem label="Both atomic">
-  ```bash # Both search and replace terms are atomic renamify rename FormAPI
-  DocSpring --atomic
+  ```bash
+  renamify rename FormAPI DocSpring --atomic
- ```bash # Only search term is atomic renamify rename FormAPI Helper - --atomic-search + ```bash + renamify rename FormAPI Helper --atomic-search ``` - -```bash # Only replace term is atomic renamify rename OldName DocSpring ---atomic-replace -``` + ```bash + renamify rename OldName DocSpring --atomic-replace + ``` ```
🤖 Prompt for AI Agents
In docs/src/content/docs/features/atomic-identifiers.mdx around lines 47–66 the
CLI examples are broken (comments and partial commands inside code fences);
replace each TabItem's code block with a proper fenced bash block containing the
full renamify commands: for "Both atomic" use renamify rename FormAPI DocSpring
--atomic, for "Search only" use renamify rename FormAPI Helper --atomic-search,
and for "Replace only" use renamify rename OldName DocSpring --atomic-replace;
ensure each block begins with ```bash on its own line and ends with ``` on its
own line so the examples render correctly.

Comment on lines 124 to 150
/// Atomic identifier arguments
#[derive(Args, Debug, Clone)]
pub struct AtomicArgs {
/// Treat both terms as atomic (single words). E.g. DocSpring becomes docspring in snake_case, not doc_spring
#[arg(long, conflicts_with_all = ["atomic_search", "atomic_replace"])]
pub atomic: bool,

/// Treat search term as atomic (DocSpring → docspring, not doc_spring)
#[arg(long)]
pub atomic_search: bool,

/// Treat replace term as atomic (DocSpring → docspring, not doc_spring)
#[arg(long)]
pub atomic_replace: bool,

/// Override config: allow word boundary detection
#[arg(long, conflicts_with = "atomic")]
pub no_atomic: bool,

/// Override config for search: allow word boundaries
#[arg(long, conflicts_with = "atomic_search")]
pub no_atomic_search: bool,

/// Override config for replace: allow word boundaries
#[arg(long, conflicts_with = "atomic_replace")]
pub no_atomic_replace: bool,
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Tighten mutually exclusive flags for atomic options

no_atomic should also conflict with atomic_search and atomic_replace to prevent contradictory inputs.

-    #[arg(long, conflicts_with = "atomic")]
+    #[arg(long, conflicts_with_all = ["atomic", "atomic_search", "atomic_replace"])]
     pub no_atomic: bool,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Atomic identifier arguments
#[derive(Args, Debug, Clone)]
pub struct AtomicArgs {
/// Treat both terms as atomic (single words). E.g. DocSpring becomes docspring in snake_case, not doc_spring
#[arg(long, conflicts_with_all = ["atomic_search", "atomic_replace"])]
pub atomic: bool,
/// Treat search term as atomic (DocSpring → docspring, not doc_spring)
#[arg(long)]
pub atomic_search: bool,
/// Treat replace term as atomic (DocSpring → docspring, not doc_spring)
#[arg(long)]
pub atomic_replace: bool,
/// Override config: allow word boundary detection
#[arg(long, conflicts_with = "atomic")]
pub no_atomic: bool,
/// Override config for search: allow word boundaries
#[arg(long, conflicts_with = "atomic_search")]
pub no_atomic_search: bool,
/// Override config for replace: allow word boundaries
#[arg(long, conflicts_with = "atomic_replace")]
pub no_atomic_replace: bool,
}
/// Atomic identifier arguments
#[derive(Args, Debug, Clone)]
pub struct AtomicArgs {
/// Treat both terms as atomic (single words). E.g. DocSpring becomes docspring in snake_case, not doc_spring
#[arg(long, conflicts_with_all = ["atomic_search", "atomic_replace"])]
pub atomic: bool,
/// Treat search term as atomic (DocSpring → docspring, not doc_spring)
#[arg(long)]
pub atomic_search: bool,
/// Treat replace term as atomic (DocSpring → docspring, not doc_spring)
#[arg(long)]
pub atomic_replace: bool,
/// Override config: allow word boundary detection
#[arg(long, conflicts_with_all = ["atomic", "atomic_search", "atomic_replace"])]
pub no_atomic: bool,
/// Override config for search: allow word boundaries
#[arg(long, conflicts_with = "atomic_search")]
pub no_atomic_search: bool,
/// Override config for replace: allow word boundaries
#[arg(long, conflicts_with = "atomic_replace")]
pub no_atomic_replace: bool,
}
🤖 Prompt for AI Agents
In renamify-cli/src/cli/args.rs around lines 124 to 150, the no_atomic flag
currently only conflicts with atomic; update its attribute to also conflict with
atomic_search and atomic_replace to avoid contradictory inputs — replace the
existing #[arg(long, conflicts_with = "atomic")] with #[arg(long,
conflicts_with_all = ["atomic", "atomic_search", "atomic_replace"])], keeping
the rest of the struct unchanged.

Comment on lines +55 to +57
None, // cwd
None, // atomic_config
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use the temp directory as working_dir to avoid a global .renamify lock.

Without passing working_dir, LockFile::acquire runs under the process CWD, risking cross-test interference and unexpected writes outside the temp dir.

Apply this diff:

-        None,   // cwd
+        Some(temp_path),   // cwd - ensure lock & plan live under the temp dir
         None,   // atomic_config
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
None, // cwd
None, // atomic_config
)
Some(temp_path), // cwd - ensure lock & plan live under the temp dir
None, // atomic_config
)
🤖 Prompt for AI Agents
In renamify-core/tests/format_string_test.rs around lines 55 to 57, the call
currently leaves working_dir as None which causes LockFile::acquire to use the
process CWD and can clash across tests; update the call to pass the test's
temporary directory as the working_dir (wrap the temp dir path in Some and
convert to the expected Path/PathBuf type as needed) so the lock is acquired
inside the temp directory and avoids a global .renamify lock.

Comment on lines +74 to +97
for m in &matches {
if m.content == "greattool" {
if let Some(ref line_before) = m.line_before {
if line_before.contains("greattool_") {
assert_eq!(
m.replace, "awesome_tool_name",
"Format string with double curly brackets should preserve snake_case, got: {}",
m.replace
);
// The full line should have the underscore preserved
if let Some(ref line_after) = m.line_after {
assert!(
line_after.contains("awesome_tool_name_"),
"Line should contain 'awesome_tool_name_', got: {}",
line_after
);
}
// Make sure it's NOT lowercase without underscores
assert_ne!(
m.replace, "awesometoolname",
"Should NOT be all lowercase without underscores"
);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

The snake_case context check looks at line_before but should inspect line_after.

For a match on "greattool" inside "greattool_{{}}.tmp", the underscore follows the match; line_after should be used. Also, asserting that line_after contains "awesome_tool_name_" is invalid because line_after reflects original text, not the replaced line.

Apply this diff:

-        if m.content == "greattool" {
-            if let Some(ref line_before) = m.line_before {
-                if line_before.contains("greattool_") {
-                    assert_eq!(
-                        m.replace, "awesome_tool_name",
-                        "Format string with double curly brackets should preserve snake_case, got: {}",
-                        m.replace
-                    );
-                    // The full line should have the underscore preserved
-                    if let Some(ref line_after) = m.line_after {
-                        assert!(
-                            line_after.contains("awesome_tool_name_"),
-                            "Line should contain 'awesome_tool_name_', got: {}",
-                            line_after
-                        );
-                    }
-                    // Make sure it's NOT lowercase without underscores
-                    assert_ne!(
-                        m.replace, "awesometoolname",
-                        "Should NOT be all lowercase without underscores"
-                    );
-                }
-            }
-        }
+        if m.content == "greattool" {
+            if let Some(ref after) = m.line_after {
+                // Underscore immediately after the match indicates snake_case context
+                if after.starts_with('_') {
+                    assert_eq!(
+                        m.replace, "awesome_tool_name",
+                        "Format string with double curly brackets should preserve snake_case, got: {}",
+                        m.replace
+                    );
+                    // Ensure the source retains the trailing underscore after the match
+                    assert!(
+                        after.starts_with('_'),
+                        "Expected '_' immediately after match, got: {}",
+                        after
+                    );
+                    // Not smashed into all lowercase without separators
+                    assert_ne!(
+                        m.replace, "awesometoolname",
+                        "Should NOT be all lowercase without underscores"
+                    );
+                }
+            }
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for m in &matches {
if m.content == "greattool" {
if let Some(ref line_before) = m.line_before {
if line_before.contains("greattool_") {
assert_eq!(
m.replace, "awesome_tool_name",
"Format string with double curly brackets should preserve snake_case, got: {}",
m.replace
);
// The full line should have the underscore preserved
if let Some(ref line_after) = m.line_after {
assert!(
line_after.contains("awesome_tool_name_"),
"Line should contain 'awesome_tool_name_', got: {}",
line_after
);
}
// Make sure it's NOT lowercase without underscores
assert_ne!(
m.replace, "awesometoolname",
"Should NOT be all lowercase without underscores"
);
}
}
for m in &matches {
if m.content == "greattool" {
if let Some(ref after) = m.line_after {
// Underscore immediately after the match indicates snake_case context
if after.starts_with('_') {
assert_eq!(
m.replace, "awesome_tool_name",
"Format string with double curly brackets should preserve snake_case, got: {}",
m.replace
);
// Ensure the source retains the trailing underscore after the match
assert!(
after.starts_with('_'),
"Expected '_' immediately after match, got: {}",
after
);
// Not smashed into all lowercase without separators
assert_ne!(
m.replace, "awesometoolname",
"Should NOT be all lowercase without underscores"
);
}
}
}
}
🤖 Prompt for AI Agents
In renamify-core/tests/format_string_test.rs around lines 74 to 97, the test
currently checks for a trailing underscore by inspecting line_before and then
asserts line_after contains the replaced string with an underscore, which is
wrong: the underscore follows the match so you must inspect line_after (not
line_before), and you must not assert line_after contains "awesome_tool_name_"
because line_after is the original context (pre-replacement). Change the check
to use line_after (e.g., assert that line_after starts_with or contains a
leading "_" immediately after the match) and remove the assertion that expects
"awesome_tool_name_" in line_after; keep the assert_eq on m.replace ==
"awesome_tool_name" and the assert_ne against "awesometoolname".

Comment on lines 78 to 85
atomicSearch: z
.boolean()
.optional()
.describe('Treat search term as atomic (indivisible unit)'),
atomicReplace: z
.boolean()
.optional()
.describe('Treat replace term as atomic (indivisible unit)'),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Atomic flags added to plan: confirm propagation and defaults

Looks good. Please confirm RenamifyTools.plan/RenamifyService.plan accept and forward atomicSearch/atomicReplace to core. If unset, service should fall back to config (not falsey).


🏁 Script executed:

#!/bin/bash
# Trace MCP -> service -> core wiring for atomic flags
rg -n --context 2 -g 'renamify-mcp/**' '\batomic(Search|Replace)\b'
rg -n --context 3 -g 'renamify-mcp/**' 'plan\(params\)'
rg -n --context 3 -g 'renamify-mcp/**' 'search\(params\)'
rg -n --context 3 -g 'renamify-mcp/**' 'rename\(params\)'

# Service/Tools mapping
rg -n -C2 -g 'renamify-mcp/**' 'class RenamifyService|class RenamifyTools|plan\(.*\)|search\(.*\)|rename\(.*\)'

# Ensure CLI/core calls receive atomic flags
rg -n -C3 -g '!**/target/**' '\batomic(Search|Replace)\b'

Length of output: 31990


Propagate atomicReplace in plan args builder
In renamify-mcp/src/renamify-service.ts’s buildPlanArgs (around line 169), only addAtomicSearchArg is called—addAtomicReplaceArg must also be invoked so the atomicReplace flag reaches the core.

🤖 Prompt for AI Agents
In renamify-mcp/src/renamify-service.ts around line 169, the buildPlanArgs
function invokes addAtomicSearchArg but never propagates the atomicReplace flag;
call addAtomicReplaceArg in the same place you call addAtomicSearchArg so the
atomicReplace option from the zod args is added to the constructed plan args
(ensure you pass the same args/plan/context parameters as used for
addAtomicSearchArg, add any missing import for addAtomicReplaceArg, and
run/update tests to cover the atomicReplace path).

Comment on lines +459 to 461
atomicSearch?: boolean;
atomicReplace?: boolean;
}): Promise<string> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Expose the rename options type or document inline shape.

Inline type is fine, but ensure MCP schema/index exports these fields and any client typings are updated.

Run:


🏁 Script executed:

#!/bin/bash
rg -n "atomic(Search|Replace)" renamify-mcp/src/index.ts renamify-mcp/src --type ts -C2

Length of output: 6312


🏁 Script executed:

#!/bin/bash
rg -n "export type" -C5 renamify-mcp/src/index.ts
rg -n "z.infer" -C5 renamify-mcp/src/index.ts
rg -n "Options" -C5 renamify-mcp/src/index.ts

Length of output: 143


Export rename options type
In renamify-mcp/src/index.ts, add and export a type alias for the rename-options schema (for example:

export type RenameOptions = z.infer<typeof renameSchema>;

) so that atomicSearch and atomicReplace are part of the public API and reflected in client typings.

🤖 Prompt for AI Agents
In renamify-mcp/src/renamify-service.ts around lines 459-461 the rename options
include atomicSearch and atomicReplace but the public API lacks a typed export;
open renamify-mcp/src/index.ts and add an exported type alias such as "export
type RenameOptions = z.infer<typeof renameSchema>;", ensuring renameSchema is
imported or referenced from its module, and then export that type from the
package entry so atomicSearch and atomicReplace are included in client typings.

Comment on lines +86 to +91
<a
href="https://docspring.github.io/renamify/features/ambiguity-resolution/"
class="more-info-link"
target="_blank"
>more info</a
>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add rel="noopener noreferrer" to external link.

Prevents reverse tabnabbing from target="_blank" in webviews.

-          <a
+          <a
             href="https://docspring.github.io/renamify/features/ambiguity-resolution/"
             class="more-info-link"
-            target="_blank"
+            target="_blank" rel="noopener noreferrer"
             >more info</a
           >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a
href="https://docspring.github.io/renamify/features/ambiguity-resolution/"
class="more-info-link"
target="_blank"
>more info</a
>
<a
href="https://docspring.github.io/renamify/features/ambiguity-resolution/"
class="more-info-link"
target="_blank" rel="noopener noreferrer"
>more info</a
>
🤖 Prompt for AI Agents
renamify-vscode/extension/templates/webview.hbs around lines 86 to 91: the
external anchor with target="_blank" lacks rel attributes; add rel="noopener
noreferrer" to the <a> tag to prevent reverse tabnabbing and ensure both
security and privacy, i.e., update the anchor element to include rel="noopener
noreferrer" alongside the existing target attribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants