Skip to content

add files#8

Merged
naheel0 merged 14 commits intomainfrom
code
Mar 3, 2026
Merged

add files#8
naheel0 merged 14 commits intomainfrom
code

Conversation

@naheel0
Copy link
Member

@naheel0 naheel0 commented Mar 3, 2026

This pull request introduces a foundational set of features for an AI-powered Git CLI tool, focusing on AI-assisted commit message generation, merge conflict resolution, GitHub PR integration, and robust configuration management. The codebase is organized into modular services for Git, AI, configuration, GitHub, and conflict resolution, and includes an interactive terminal UI for PR selection. Below are the most important changes:

AI Integration and Commit Automation

  • Implements AIService to generate conventional commit messages and analyze merge conflicts using Google Gemini, with robust error handling and prompt engineering. (git-ai/src/services/AIService.ts)
  • Adds the ai-commit CLI command to stage changes, generate an AI commit message, and commit via the terminal. (git-ai/src/index.ts)

Git and Conflict Resolution Services

  • Introduces GitService for core git operations (status, diff, commit, log, branch), abstracting simple-git for use across commands. (git-ai/src/core/GitService.ts)
  • Adds ConflictResolver to detect conflicted files, use AI to suggest resolutions, and safely apply them with atomic writes and symlink protection. (git-ai/src/services/ConflictResolver.ts)
  • Provides a CLI command to analyze and suggest AI-powered resolutions for merge conflicts. (git-ai/src/cli/resolve-command.ts)

GitHub Pull Request Integration

  • Implements GitHubService to fetch open PRs and merge them using the GitHub API, parsing repo info from git remotes and handling authentication. (git-ai/src/services/GitHubService.ts)
  • Adds an Ink-based TUI (PRList) for interactive PR selection, with keyboard navigation and selection callbacks. (git-ai/src/ui/PRList.tsx, git-ai/src/cli/pr-command.ts) [1] [2]

Configuration and Logging

  • Adds ConfigService for loading, validating (with Zod), and saving user config from ~/.aigitrc, supporting AI, GitHub, git, and UI settings. (git-ai/src/services/ConfigService.ts)
  • Introduces a centralized logger using pino for structured, colorized logging across all services. (git-ai/src/utils/logger.ts)

Summary by CodeRabbit

  • New Features
    • AI-assisted commit workflow, plus CLI commands for PR browsing and conflict resolution
    • Interactive PR browser with keyboard navigation and selection
    • Automated merge conflict detection with AI-suggested resolutions and safe apply
    • Git operations (status, diff, commit, log) and GitHub integration for listing/merging PRs
    • AI service for commit messages, conflict analysis, and content generation
  • Chores
    • Config management with validated user config, structured logging, TypeScript config updates, and added runtime integrations

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

📝 Walkthrough

Walkthrough

Adds multiple new CLI commands, services, an Ink-based PR UI, AI (Gemini) integration, GitHub integration, conflict-resolution utilities, configuration handling, and a pino logger to enable AI-assisted commits, PR browsing, and automated conflict suggestions.

Changes

Cohort / File(s) Summary
Core services
src/core/GitService.ts, src/services/ConfigService.ts, src/services/AIService.ts, src/services/GitHubService.ts, src/services/ConflictResolver.ts
New service implementations: GitService (simple-git wrapper), ConfigService (zod-backed ~/.aigitrc), AIService (Gemini client & AIProvider), GitHubService (Octokit-based PR list/merge), ConflictResolver (conflict extraction, sanitization, AI suggestion, atomic apply).
CLI entrypoints
src/cli/pr-command.ts, src/cli/resolve-command.ts, src/index.ts
New CLI commands: runPRCommand() (renders Ink PRList, wires GitHubService), runResolveCommand() (discovers merge conflicts, requests AI suggestions per conflict), and a Commander-based ai-commit entrypoint that uses AIService to generate and apply commit messages.
UI
src/ui/PRList.tsx
Ink/React PRList component that lists open PRs via GitHubService, supports keyboard navigation and selection, and reports the selected PR via an onSelect handler.
Utilities & config
src/utils/logger.ts, tsconfig.json, package.json
Adds a pino logger configured with pino-pretty, updates tsconfig for ESM/JSON/sourceMap and test excludes, and adds runtime deps: @google/generative-ai, @octokit/rest, pino, pino-pretty, zod.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PRCmd as pr-command
    participant Config as ConfigService
    participant Git as GitService
    participant GH as GitHubService
    participant UI as PRList
    participant Handler as SelectionHandler

    User->>PRCmd: runPRCommand()
    PRCmd->>Config: getConfig()
    PRCmd->>Git: git.getRemotes(true)
    PRCmd->>Git: validate origin fetch URL
    PRCmd->>GH: init(repoUrl)
    PRCmd->>UI: render(githubService, onSelect)
    UI->>GH: listOpenPRs()
    GH-->>UI: PR metadata
    User->>UI: navigate & select
    UI->>Handler: onSelect(selectedPR)
    Handler-->>User: print PR details & unmount
Loading
sequenceDiagram
    participant User
    participant ResolveCmd as resolve-command
    participant Config as ConfigService
    participant Git as GitService
    participant Resolver as ConflictResolver
    participant AI as AIService

    User->>ResolveCmd: runResolveCommand()
    ResolveCmd->>Config: getConfig()
    ResolveCmd->>Git: init/getStatus()
    ResolveCmd->>AI: initClient()
    ResolveCmd->>Resolver: new Resolver(aiService, gitService)
    ResolveCmd->>Resolver: getConflicts()
    Resolver->>Git: read conflicted files
    Git-->>Resolver: file contents
    loop per conflict
      Resolver->>AI: suggestResolution(conflict)
      AI-->>Resolver: resolvedContent
      Resolver->>Git: applyResolution(atomically)
    end
    Resolver-->>ResolveCmd: results
    ResolveCmd-->>User: report results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇
I hopped through diffs with twitching nose,
Ink-lit lists and AI prose,
I mended merges, nudged commits,
And left a trail of tiny bits. 🥕

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is comprehensive and well-structured, covering all major components. However, it does not follow the provided template structure with required sections like 'Related Issue', 'Type of Change', and 'Checklist'. Reformat the description to match the template: add 'Related Issue' section, mark applicable checkboxes in 'Type of Change' (likely 'New feature'), complete the 'Checklist', and include 'How Has This Been Tested?' and 'Additional Notes' sections.
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'add files' is vague and generic, failing to convey the primary purpose of this substantial changeset introducing AI-powered Git CLI features. Replace with a specific, descriptive title reflecting the main objective, such as 'Add AI-powered Git CLI with commit automation, conflict resolution, and GitHub PR integration' or 'Introduce foundational AI Git CLI services and commands'.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jaseel0
Copy link
Collaborator

jaseel0 commented Mar 3, 2026

@copilot
Run npm run lint --if-present

git-ai@1.0.0 lint
tsc --noEmit

Error: src/services/AIService.ts(1,53): error TS2307: Cannot find module '@google/generative-ai' or its corresponding type declarations.
Error: src/services/ConfigService.ts(4,19): error TS2307: Cannot find module 'zod' or its corresponding type declarations.
Error: src/services/GitHubService.ts(1,25): error TS2307: Cannot find module '@octokit/rest' or its corresponding type declarations.
Error: src/services/GitHubService.ts(67,30): error TS7006: Parameter 'pr' implicitly has an 'any' type.
Error: src/utils/logger.ts(1,18): error TS2307: Cannot find module 'pino' or its corresponding type declarations.
Error: Process completed with exit code 2.

Run actions/setup-node@v4
Attempting to download 18.x...
Acquiring 18.20.8 - x64 from https://github.com/actions/node-versions/releases/download/18.20.8-14110393767/node-18.20.8-linux-x64.tar.gz
Extracting ...
/usr/bin/tar xz --strip 1 --warning=no-unknown-keyword --overwrite -C /home/runner/work/_temp/63fa74af-7d28-4c24-be74-cdaa4f5e2751 -f /home/runner/work/_temp/33540a9a-ebf5-45b5-bd92-1c3e8bf72b5e
Adding to the cache ...
Environment details
/opt/hostedtoolcache/node/18.20.8/x64/bin/npm config get cache
/home/runner/.npm
Cache hit for: node-cache-Linux-x64-npm-cb4926b83d1d80aabcd83cba1d9820012e13c549eacfad836bb3fa1102e52601
Received 11351099 of 11351099 (100.0%), 20.5 MBs/sec
Cache Size: ~11 MB (11351099 B)
/usr/bin/tar -xf /home/runner/work/_temp/cd5a9891-5d56-45d1-8727-1355d9279894/cache.tzst -P -C /home/runner/work/git-ai/git-ai --use-compress-program unzstd
Error: The operation was canceled.

Copy link
Contributor

Copilot AI commented Mar 3, 2026

@jaseel0 I've opened a new pull request, #9, to work on those changes. Once the pull request is ready, I'll request review from you.

Co-authored-by: jaseel0 <225665919+jaseel0@users.noreply.github.com>
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: 9

🧹 Nitpick comments (2)
git-ai/src/cli/resolve-command.ts (1)

27-29: Avoid dumping full resolved file content to stdout by default.

Line [28] can expose sensitive source in terminal scrollback/log collectors and can overwhelm output for large files. Consider a preview + explicit “show full” option.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@git-ai/src/cli/resolve-command.ts` around lines 27 - 29, The current
console.log of the full resolved content (the lines printing suggestion and the
header with conflict.file) can leak large or sensitive files; change this to
print a short preview instead and require an explicit flag or confirmation to
dump the full content. Replace the direct console.log(suggestion) usage with a
preview (e.g., first N lines or chars) and add a CLI flag like --show-full (or
an interactive prompt) that, when set, prints the entire suggestion; update the
code paths around the console.log calls and the suggestion variable handling in
resolve-command.ts so the header still references conflict.file but full output
is gated behind the new flag/confirmation.
git-ai/src/cli/pr-command.ts (1)

19-20: Add a public getRemotes() method to GitService instead of bypassing encapsulation with as any.

The code at lines 19–20 uses (gitService as any).git.getRemotes(true) to access the private git field, which violates encapsulation and is fragile if internals change. GitService has no public getRemotes() wrapper method. Add one:

// git-ai/src/core/GitService.ts
public async getRemotes(verbose = true) {
  return await this.git.getRemotes(verbose);
}

Then replace the call at line 19 with await gitService.getRemotes(true) to remove the as any cast.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@git-ai/src/cli/pr-command.ts` around lines 19 - 20, Add a public wrapper on
the GitService class that exposes the underlying getRemotes call (e.g., add a
public async getRemotes(verbose = true) method in GitService that returns
this.git.getRemotes(verbose)), then update pr-command.ts to call await
gitService.getRemotes(true) instead of using (gitService as
any).git.getRemotes(true), removing the as any cast and preserving
encapsulation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@git-ai/src/cli/pr-command.ts`:
- Around line 22-24: The error branch that checks for origin (the if block
referencing origin and origin.refs.fetch in pr-command.ts) logs an error but
returns normally; change it to return a non-zero exit status for CLI callers by
exiting with a failure code (e.g., call process.exit(1)) or by throwing an error
so the process terminates with a non-zero status; update the branch inside the
function that contains this check (the origin validation block) to use
process.exit(1) or rethrow instead of simply returning.
- Around line 30-39: The selection handler handleSelect currently calls
process.exit(0) which forces termination; instead obtain the Ink render instance
and call its unmount() so the app can perform a clean teardown and allow
waitUntilExit() to complete; update handleSelect to call
renderInstance.unmount() (and optionally trigger gitService.checkout(pr.branch)
before unmounting), then return so the process exits naturally after
waitUntilExit() finishes.

In `@git-ai/src/index.ts`:
- Around line 8-10: AIService is being instantiated at startup which can crash
CLI bootstrap if config is missing; instead, delay construction until the
ai-commit command runs. Remove the top-level new AIService(configService)
instantiation and instead create a local AIService instance inside the ai-commit
command action (and any other command handlers that currently new AIService,
e.g., the other spots referenced around the same block), passing the existing
ConfigService instance; ensure GitService and ConfigService remain created at
startup but AIService construction is deferred to inside the command handlers to
avoid early crashes.

In `@git-ai/src/services/AIService.ts`:
- Line 1: The project is missing the `@google/generative-ai` dependency referenced
by the import in AIService.ts (GoogleGenerativeAI, GenerativeModel); add
"@google/generative-ai" to the dependencies section of package.json, run the
package manager to update node_modules and the lockfile (npm install or yarn
install), and ensure package.json and the lockfile are committed so the build
can resolve the import.
- Around line 25-39: The constructor/initialization in AIService currently only
handles config.ai.provider === "gemini" and leaves this.model undefined for
other providers; update the initialization logic (referencing
config.ai.provider, this.genAI, this.model) to fail fast for unsupported
providers by adding an explicit else/default branch that throws a clear Error
including the unsupported provider value (e.g., `Unsupported AI provider:
${config.ai.provider}`), or alternatively implement the "openai" branch if
intended; ensure the thrown error occurs during service construction so later
calls cannot proceed with a null model.

In `@git-ai/src/services/ConfigService.ts`:
- Around line 56-59: The saveConfig method currently writes secrets to disk
without restrictive permissions; update saveConfig (and the write path that uses
ConfigService.CONFIG_PATH) to write the file with owner-only permissions (mode
0o600) by passing a mode option to fs.writeFileSync or by writing to a temp file
and fs.renameSync while setting file mode via fs.chmodSync/fchmod to 0o600 so
the resulting config file is readable/writable only by the owner; ensure you
still update this.config with the validated value after a successful write.

In `@git-ai/src/services/ConflictResolver.ts`:
- Around line 55-62: The suggestResolution method currently injects raw
conflict.content (and conflict.file) into the external model prompt which can
leak secrets/PII; implement a sanitization step (e.g., a new sanitizeContent or
redactSecrets function) inside suggestResolution to scan conflict.content for
common secret patterns (API keys, tokens, private key blocks, passwords, emails,
and other sensitive regexes) and either redact/mask them or replace large
sensitive blocks with placeholder text, and limit the payload by extracting only
the conflict hunk around git markers (<<<<<<<, =======, >>>>>>>) rather than the
entire file; ensure the sanitized content is what gets sent to the model and
keep ConflictDetail usage but pass sanitizedContent into the prompt.
- Around line 85-86: The current write path in ConflictResolver is not truly
atomic despite the comment; update the file-write routine in the
ConflictResolver class (the method that currently opens the target with O_TRUNC
and calls write()) to use the atomic-replace pattern: create a uniquely named
temporary file in the same directory (open with O_CREAT|O_EXCL|O_NOFOLLOW),
write the full contents to that temp file, call fsync on the temp file, close
it, call fsync on the directory, then rename(tempPath, targetPath) to atomically
replace the target; on any error ensure the temp file is unlinked and surface
the error. Ensure file mode/ownership are preserved or set appropriately before
rename.

In `@git-ai/src/services/GitHubService.ts`:
- Around line 45-46: The logger.error currently emits the raw remote URL (e.g.,
in GitHubService.ts where logger.error is called), which can leak credentials;
update the code that logs "Could not parse GitHub owner/repo from remote URL"
(and the similar call around lines 50–51) to sanitize the URL before logging by
parsing the URL (e.g., via the URL constructor or equivalent) and stripping any
username/password/userinfo component, then log the redacted/sanitized URL or
only the host/path instead of the raw value; keep the thrown Error message
unchanged but ensure all logger.error calls (the ones referencing the remote
URL) use the sanitized variable.

---

Nitpick comments:
In `@git-ai/src/cli/pr-command.ts`:
- Around line 19-20: Add a public wrapper on the GitService class that exposes
the underlying getRemotes call (e.g., add a public async getRemotes(verbose =
true) method in GitService that returns this.git.getRemotes(verbose)), then
update pr-command.ts to call await gitService.getRemotes(true) instead of using
(gitService as any).git.getRemotes(true), removing the as any cast and
preserving encapsulation.

In `@git-ai/src/cli/resolve-command.ts`:
- Around line 27-29: The current console.log of the full resolved content (the
lines printing suggestion and the header with conflict.file) can leak large or
sensitive files; change this to print a short preview instead and require an
explicit flag or confirmation to dump the full content. Replace the direct
console.log(suggestion) usage with a preview (e.g., first N lines or chars) and
add a CLI flag like --show-full (or an interactive prompt) that, when set,
prints the entire suggestion; update the code paths around the console.log calls
and the suggestion variable handling in resolve-command.ts so the header still
references conflict.file but full output is gated behind the new
flag/confirmation.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9627705 and 6bf3b19.

📒 Files selected for processing (11)
  • git-ai/src/cli/pr-command.ts
  • git-ai/src/cli/resolve-command.ts
  • git-ai/src/core/GitService.ts
  • git-ai/src/index.ts
  • git-ai/src/services/AIService.ts
  • git-ai/src/services/ConfigService.ts
  • git-ai/src/services/ConflictResolver.ts
  • git-ai/src/services/GitHubService.ts
  • git-ai/src/ui/PRList.tsx
  • git-ai/src/utils/logger.ts
  • git-ai/tsconfig.json

@jaseel0
Copy link
Collaborator

jaseel0 commented Mar 3, 2026

@copilot
Verify each finding against the current code and only fix it if needed.

In @git-ai/src/cli/pr-command.ts around lines 22 - 24, The error branch that
checks for origin (the if block referencing origin and origin.refs.fetch in
pr-command.ts) logs an error but returns normally; change it to return a
non-zero exit status for CLI callers by exiting with a failure code (e.g., call
process.exit(1)) or by throwing an error so the process terminates with a
non-zero status; update the branch inside the function that contains this check
(the origin validation block) to use process.exit(1) or rethrow instead of
simply returning.

Verify each finding against the current code and only fix it if needed.

In @git-ai/src/cli/pr-command.ts around lines 30 - 39, The selection handler
handleSelect currently calls process.exit(0) which forces termination; instead
obtain the Ink render instance and call its unmount() so the app can perform a
clean teardown and allow waitUntilExit() to complete; update handleSelect to
call renderInstance.unmount() (and optionally trigger
gitService.checkout(pr.branch) before unmounting), then return so the process
exits naturally after waitUntilExit() finishes.
Verify each finding against the current code and only fix it if needed.

In @git-ai/src/index.ts around lines 8 - 10, AIService is being instantiated at
startup which can crash CLI bootstrap if config is missing; instead, delay
construction until the ai-commit command runs. Remove the top-level new
AIService(configService) instantiation and instead create a local AIService
instance inside the ai-commit command action (and any other command handlers
that currently new AIService, e.g., the other spots referenced around the same
block), passing the existing ConfigService instance; ensure GitService and
ConfigService remain created at startup but AIService construction is deferred
to inside the command handlers to avoid early crashes.

Verify each finding against the current code and only fix it if needed.

In @git-ai/src/services/AIService.ts at line 1, The project is missing the
@google/generative-ai dependency referenced by the import in AIService.ts
(GoogleGenerativeAI, GenerativeModel); add "@google/generative-ai" to the
dependencies section of package.json, run the package manager to update
node_modules and the lockfile (npm install or yarn install), and ensure
package.json and the lockfile are committed so the build can resolve the import.

Verify each finding against the current code and only fix it if needed.

In @git-ai/src/services/AIService.ts around lines 25 - 39, The
constructor/initialization in AIService currently only handles
config.ai.provider === "gemini" and leaves this.model undefined for other
providers; update the initialization logic (referencing config.ai.provider,
this.genAI, this.model) to fail fast for unsupported providers by adding an
explicit else/default branch that throws a clear Error including the unsupported
provider value (e.g., Unsupported AI provider: ${config.ai.provider}), or
alternatively implement the "openai" branch if intended; ensure the thrown error
occurs during service construction so later calls cannot proceed with a null
model.

Verify each finding against the current code and only fix it if needed.

In @git-ai/src/services/ConfigService.ts around lines 56 - 59, The saveConfig
method currently writes secrets to disk without restrictive permissions; update
saveConfig (and the write path that uses ConfigService.CONFIG_PATH) to write the
file with owner-only permissions (mode 0o600) by passing a mode option to
fs.writeFileSync or by writing to a temp file and fs.renameSync while setting
file mode via fs.chmodSync/fchmod to 0o600 so the resulting config file is
readable/writable only by the owner; ensure you still update this.config with
the validated value after a successful write.

Verify each finding against the current code and only fix it if needed.

In @git-ai/src/services/ConflictResolver.ts around lines 55 - 62, The
suggestResolution method currently injects raw conflict.content (and
conflict.file) into the external model prompt which can leak secrets/PII;
implement a sanitization step (e.g., a new sanitizeContent or redactSecrets
function) inside suggestResolution to scan conflict.content for common secret
patterns (API keys, tokens, private key blocks, passwords, emails, and other
sensitive regexes) and either redact/mask them or replace large sensitive blocks
with placeholder text, and limit the payload by extracting only the conflict
hunk around git markers (<<<<<<<, =======, >>>>>>>) rather than the entire file;
ensure the sanitized content is what gets sent to the model and keep
ConflictDetail usage but pass sanitizedContent into the prompt.

Verify each finding against the current code and only fix it if needed.

In @git-ai/src/services/ConflictResolver.ts around lines 85 - 86, The current
write path in ConflictResolver is not truly atomic despite the comment; update
the file-write routine in the ConflictResolver class (the method that currently
opens the target with O_TRUNC and calls write()) to use the atomic-replace
pattern: create a uniquely named temporary file in the same directory (open with
O_CREAT|O_EXCL|O_NOFOLLOW), write the full contents to that temp file, call
fsync on the temp file, close it, call fsync on the directory, then
rename(tempPath, targetPath) to atomically replace the target; on any error
ensure the temp file is unlinked and surface the error. Ensure file
mode/ownership are preserved or set appropriately before rename.

Verify each finding against the current code and only fix it if needed.

In @git-ai/src/services/GitHubService.ts around lines 45 - 46, The logger.error
currently emits the raw remote URL (e.g., in GitHubService.ts where logger.error
is called), which can leak credentials; update the code that logs "Could not
parse GitHub owner/repo from remote URL" (and the similar call around lines
50–51) to sanitize the URL before logging by parsing the URL (e.g., via the URL
constructor or equivalent) and stripping any username/password/userinfo
component, then log the redacted/sanitized URL or only the host/path instead of
the raw value; keep the thrown Error message unchanged but ensure all
logger.error calls (the ones referencing the remote URL) use the sanitized
variable.

Copy link
Contributor

Copilot AI commented Mar 3, 2026

@jaseel0 I've opened a new pull request, #10, to work on those changes. Once the pull request is ready, I'll request review from you.

naheel0 and others added 4 commits March 3, 2026 21:42
Fix missing npm dependencies causing TypeScript lint failures
Co-authored-by: jaseel0 <225665919+jaseel0@users.noreply.github.com>
Fix missing dependencies, CLI exit behavior, and security issues
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: 1

♻️ Duplicate comments (2)
git-ai/src/services/ConflictResolver.ts (2)

28-49: ⚠️ Potential issue | 🟠 Major

Do not fall back to full file content when no conflict markers exist.

Line [48] returns the entire file when markers are absent, which bypasses the hunk-only minimization and can leak unrelated sensitive data to the model.

♻️ Proposed fix
 function extractConflictHunks(content: string): string {
@@
-  return hunks.length > 0 ? hunks.join('\n\n') : content;
+  return hunks.length > 0 ? hunks.join('\n\n') : '';
 }
@@
 function sanitizeContent(content: string): string {
   let sanitized = extractConflictHunks(content);
+  if (!sanitized) {
+    return '[NO_CONFLICT_MARKERS_FOUND]';
+  }
   for (const pattern of SECRET_PATTERNS) {
     sanitized = sanitized.replace(pattern, '[REDACTED]');
   }
   return sanitized;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@git-ai/src/services/ConflictResolver.ts` around lines 28 - 49, The function
extractConflictHunks currently returns the entire file when no conflict markers
exist, which can leak unrelated data; update extractConflictHunks so it returns
only the joined conflict hunks when any are found and returns an empty string
(not the full content) when none are found, i.e., replace the final return
branch to return hunks.length > 0 ? hunks.join('\n\n') : '' and ensure callers
of extractConflictHunks (the code that calls this function) properly handle an
empty string result.

145-176: ⚠️ Potential issue | 🟠 Major

Preserve target file permissions instead of forcing 0o644.

Line [176] always creates the temp file with 0o644. On replace, this can broaden sensitive file permissions or drop executable bits.

♻️ Proposed fix
   public async applyResolution(file: string, resolvedContent: string): Promise<void> {
@@
+    let targetMode = 0o644;
     try {
       const targetStats = await fs.lstat(targetPath);
       if (targetStats.isSymbolicLink()) {
         throw new Error(`Refusing to write to symlink: ${file}`);
       }
+      targetMode = targetStats.mode & 0o777;
     } catch (error: unknown) {
@@
-    const tempHandle = await fs.open(tempPath, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW, 0o644);
+    const tempHandle = await fs.open(
+      tempPath,
+      fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW,
+      targetMode
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@git-ai/src/services/ConflictResolver.ts` around lines 145 - 176, The temp
file creation currently forces mode 0o644 when opening tempPath; instead reuse
the existing target's permission bits if the target existed (use the earlier
targetStats.mode & 0o777) and only fall back to a sensible umask-respecting
default when targetStats is absent (e.g. (0o666 & ~process.umask())). Replace
the hardcoded 0o644 in the fs.open call that creates tempPath with the computed
mode variable so we preserve executable bits and avoid widening or dropping
permissions.
🧹 Nitpick comments (1)
git-ai/src/cli/pr-command.ts (1)

17-25: Avoid bypassing encapsulation with as any to access private members.

Line 19 uses (gitService as any).git to access the private git instance, and Line 20 uses r: any which loses type safety. This pattern is fragile – if GitService internals change, this code silently breaks at runtime.

Consider exposing a public method on GitService:

♻️ Suggested approach

Add a method to GitService (in git-ai/src/core/GitService.ts):

public async getRemotes(): Promise<RemoteWithRefs[]> {
  return await this.git.getRemotes(true);
}

Then update pr-command.ts:

-    const remotes = await (gitService as any).git.getRemotes(true);
-    const origin = remotes.find((r: any) => r.name === 'origin');
+    const remotes = await gitService.getRemotes();
+    const origin = remotes.find((r) => r.name === 'origin');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@git-ai/src/cli/pr-command.ts` around lines 17 - 25, Replace the unsafe casts
by adding a public accessor on GitService named getRemotes that returns the
typed remotes (e.g., public async getRemotes(): Promise<RemoteWithRefs[]> {
return this.git.getRemotes(true); }) and then in the PR command call
gitService.getRemotes() (remove (gitService as any).git and r: any) and use the
proper RemoteWithRefs type for remotes and origin when finding name === 'origin'
so you keep type safety and avoid accessing private members directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@git-ai/src/services/ConflictResolver.ts`:
- Around line 177-179: The current write via tempHandle.write(...) can perform a
short write and cause truncated resolved files; replace the manual write+sync
sequence in ConflictResolver (the tempHandle.write and tempHandle.sync usage)
with a full-file write that retries until the buffer is written (e.g., use
fs.promises.writeFile or the FileHandle.writeFile helper) to atomically write
the entire buffer to the temporary path, then close the handle and proceed to
the rename; ensure you still close/cleanup tempHandle (and preserve the existing
rename logic) so the temporary file is complete before renaming.

---

Duplicate comments:
In `@git-ai/src/services/ConflictResolver.ts`:
- Around line 28-49: The function extractConflictHunks currently returns the
entire file when no conflict markers exist, which can leak unrelated data;
update extractConflictHunks so it returns only the joined conflict hunks when
any are found and returns an empty string (not the full content) when none are
found, i.e., replace the final return branch to return hunks.length > 0 ?
hunks.join('\n\n') : '' and ensure callers of extractConflictHunks (the code
that calls this function) properly handle an empty string result.
- Around line 145-176: The temp file creation currently forces mode 0o644 when
opening tempPath; instead reuse the existing target's permission bits if the
target existed (use the earlier targetStats.mode & 0o777) and only fall back to
a sensible umask-respecting default when targetStats is absent (e.g. (0o666 &
~process.umask())). Replace the hardcoded 0o644 in the fs.open call that creates
tempPath with the computed mode variable so we preserve executable bits and
avoid widening or dropping permissions.

---

Nitpick comments:
In `@git-ai/src/cli/pr-command.ts`:
- Around line 17-25: Replace the unsafe casts by adding a public accessor on
GitService named getRemotes that returns the typed remotes (e.g., public async
getRemotes(): Promise<RemoteWithRefs[]> { return this.git.getRemotes(true); })
and then in the PR command call gitService.getRemotes() (remove (gitService as
any).git and r: any) and use the proper RemoteWithRefs type for remotes and
origin when finding name === 'origin' so you keep type safety and avoid
accessing private members directly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6bf3b19 and bc03c6e.

⛔ Files ignored due to path filters (1)
  • git-ai/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • git-ai/package.json
  • git-ai/src/cli/pr-command.ts
  • git-ai/src/index.ts
  • git-ai/src/services/AIService.ts
  • git-ai/src/services/ConfigService.ts
  • git-ai/src/services/ConflictResolver.ts
  • git-ai/src/services/GitHubService.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • git-ai/src/index.ts
  • git-ai/src/services/ConfigService.ts

@jaseel0
Copy link
Collaborator

jaseel0 commented Mar 3, 2026

@copilot
Verify each finding against the current code and only fix it if needed.

In @git-ai/src/services/AIService.ts at line 1, The project is missing the
@google/generative-ai dependency referenced by the import in AIService.ts
(GoogleGenerativeAI, GenerativeModel); add "@google/generative-ai" to the
dependencies section of package.json, run the package manager to update
node_modules and the lockfile (npm install or yarn install), and ensure
package.json and the lockfile are committed so the build can resolve the import.

Verify each finding against the current code and only fix it if needed.

In @git-ai/src/services/ConflictResolver.ts around lines 177 - 179, The current
write via tempHandle.write(...) can perform a short write and cause truncated
resolved files; replace the manual write+sync sequence in ConflictResolver (the
tempHandle.write and tempHandle.sync usage) with a full-file write that retries
until the buffer is written (e.g., use fs.promises.writeFile or the
FileHandle.writeFile helper) to atomically write the entire buffer to the
temporary path, then close the handle and proceed to the rename; ensure you
still close/cleanup tempHandle (and preserve the existing rename logic) so the
temporary file is complete before renaming.

Copy link
Contributor

Copilot AI commented Mar 3, 2026

@jaseel0 I've opened a new pull request, #11, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 2 commits March 3, 2026 17:21
…esolver

Co-authored-by: jaseel0 <225665919+jaseel0@users.noreply.github.com>
fix: install @google/generative-ai and use writeFile for atomic writes in ConflictResolver
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@git-ai/src/services/ConflictResolver.ts`:
- Around line 173-185: The atomic write is using a hardcoded mode 0o644 which
strips execute bits; instead stat the existing target file (use fs.stat or
fs.lstat on the target path) to obtain its mode, mask it with 0o777 and use that
mode when creating/opening the temp file (replace the hardcoded 0o644 passed to
fs.open where tempHandle is created). If the target does not exist, fall back to
0o644; keep the existing error cleanup logic (closing/unlink) intact.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc03c6e and a22ffaa.

⛔ Files ignored due to path filters (1)
  • git-ai/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • git-ai/src/services/ConflictResolver.ts

@jaseel0
Copy link
Collaborator

jaseel0 commented Mar 3, 2026

@copilot
Verify each finding against the current code and only fix it if needed.

In @git-ai/src/services/ConflictResolver.ts around lines 173 - 185, The atomic
write is using a hardcoded mode 0o644 which strips execute bits; instead stat
the existing target file (use fs.stat or fs.lstat on the target path) to obtain
its mode, mask it with 0o777 and use that mode when creating/opening the temp
file (replace the hardcoded 0o644 passed to fs.open where tempHandle is
created). If the target does not exist, fall back to 0o644; keep the existing
error cleanup logic (closing/unlink) intact.

Copy link
Contributor

Copilot AI commented Mar 3, 2026

@jaseel0 I've opened a new pull request, #12, to work on those changes. Once the pull request is ready, I'll request review from you.

Co-authored-by: jaseel0 <225665919+jaseel0@users.noreply.github.com>
fix: preserve file permissions in ConflictResolver atomic write
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.

♻️ Duplicate comments (3)
git-ai/src/services/ConflictResolver.ts (3)

48-48: ⚠️ Potential issue | 🟠 Major

Avoid full-file fallback when conflict markers are missing.

Line [48] returns the entire file when no marker block is found, which defeats the hunk-only data minimization path.

🔒 Proposed safer fallback
-  return hunks.length > 0 ? hunks.join('\n\n') : content;
+  if (hunks.length > 0) {
+    return hunks.join('\n\n');
+  }
+  return '';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@git-ai/src/services/ConflictResolver.ts` at line 48, The current return
expression "hunks.length > 0 ? hunks.join('\n\n') : content" in ConflictResolver
returns the whole file when no conflict markers are found; change it to avoid
full-file fallback by returning an empty string (or null/undefined per project
convention) instead of content when hunks.length === 0, and update any callers
of the ConflictResolver method that expect a string (e.g., callers of the
function containing this return) to handle the empty/no-hunk case appropriately
(validate output before using, skip processing or fall back to the hunk-only
minimization path).

173-200: ⚠️ Potential issue | 🟠 Major

Complete crash-safe atomic replace with directory sync.

Lines [195]-[200] rename the temp file without syncing parent directory metadata. A crash window remains where data is written but rename metadata is not durable.

💾 Proposed durability fix
-    // Atomically replace target with temp file
-    try {
-      await fs.rename(tempPath, targetPath);
+    // Atomically replace target with temp file and sync dir metadata
+    const dirHandle = await fs.open(targetParent, fs.constants.O_RDONLY);
+    try {
+      await dirHandle.sync();
+      await fs.rename(tempPath, targetPath);
+      await dirHandle.sync();
     } catch (error) {
       await fs.unlink(tempPath).catch(() => {});
       throw error;
+    } finally {
+      await dirHandle.close().catch(() => {});
     }
#!/bin/bash
set -euo pipefail

FILE="git-ai/src/services/ConflictResolver.ts"

echo "=== applyResolution write/rename block ==="
cat -n "$FILE" | sed -n '170,210p'

echo
echo "=== sync/open/rename calls in this file ==="
rg -n "fs\\.open\\(|\\.sync\\(|fs\\.rename\\(" "$FILE"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@git-ai/src/services/ConflictResolver.ts` around lines 173 - 200, The
write/rename sequence is missing a parent-directory fsync, leaving a durability
window; after successfully renaming tempPath to targetPath, open the parent
directory (targetParent) with fs.open(..., fs.constants.O_DIRECTORY |
fs.constants.O_RDONLY), call dirHandle.sync() (or fsync) to flush the rename
metadata, then close the dir handle; ensure this happens in the same try that
performs fs.rename and that you still clean up tempPath on errors (references:
tempPath, targetParent, targetPath, tempHandle.sync(), fs.rename).

17-23: ⚠️ Potential issue | 🟠 Major

Expand redaction coverage before sending content to the model.

Line [18] and Line [23] still allow common sensitive data classes through (notably emails and non-RSA private-key block variants).

🛡️ Proposed hardening
 const SECRET_PATTERNS: RegExp[] = [
-  /-----BEGIN\s+(?:RSA\s+)?PRIVATE KEY-----[\s\S]*?-----END\s+(?:RSA\s+)?PRIVATE KEY-----/gi,
+  /-----BEGIN\s+[A-Z ]*PRIVATE KEY-----[\s\S]*?-----END\s+[A-Z ]*PRIVATE KEY-----/g,
   /(?:api[_\s-]?key|apikey|secret|token|password|passwd|pwd|auth)['":\s=]+['"]?[A-Za-z0-9/+_\-]{16,}['"]?/gi,
   // Long base64-like strings that resemble encoded tokens (standalone, not part of identifiers)
   /(?<![A-Za-z0-9])([A-Za-z0-9+/]{40,}={0,2})(?![A-Za-z0-9])/g,
   /(?<![A-Za-z0-9_])[a-f0-9]{32,64}(?![A-Za-z0-9_])/gi, // hex strings (hashes/keys)
+  /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, // emails
 ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@git-ai/src/services/ConflictResolver.ts` around lines 17 - 23,
SECRET_PATTERNS currently misses common sensitive classes (emails,
SSH/PGP/non‑RSA private key block variants, and token/key formats like AWS
access keys), so update the SECRET_PATTERNS array in ConflictResolver.ts to
include additional regexes that match email addresses, SSH/PGP/private-key
blocks (-----BEGIN [A-Z ]*PRIVATE KEY----- with various key names), AWS-style
access keys (AKIA[0-9A-Z]{16}), and common token formats (shorter base64-like or
dot-separated JWTs), keeping appropriate flags (g, i) and using word-boundary or
lookaround anchors similar to existing entries; ensure these new patterns are
applied wherever SECRET_PATTERNS is used before sending content to the model
(e.g., in any redact/clean function) so redaction covers these classes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@git-ai/src/services/ConflictResolver.ts`:
- Line 48: The current return expression "hunks.length > 0 ? hunks.join('\n\n')
: content" in ConflictResolver returns the whole file when no conflict markers
are found; change it to avoid full-file fallback by returning an empty string
(or null/undefined per project convention) instead of content when hunks.length
=== 0, and update any callers of the ConflictResolver method that expect a
string (e.g., callers of the function containing this return) to handle the
empty/no-hunk case appropriately (validate output before using, skip processing
or fall back to the hunk-only minimization path).
- Around line 173-200: The write/rename sequence is missing a parent-directory
fsync, leaving a durability window; after successfully renaming tempPath to
targetPath, open the parent directory (targetParent) with fs.open(...,
fs.constants.O_DIRECTORY | fs.constants.O_RDONLY), call dirHandle.sync() (or
fsync) to flush the rename metadata, then close the dir handle; ensure this
happens in the same try that performs fs.rename and that you still clean up
tempPath on errors (references: tempPath, targetParent, targetPath,
tempHandle.sync(), fs.rename).
- Around line 17-23: SECRET_PATTERNS currently misses common sensitive classes
(emails, SSH/PGP/non‑RSA private key block variants, and token/key formats like
AWS access keys), so update the SECRET_PATTERNS array in ConflictResolver.ts to
include additional regexes that match email addresses, SSH/PGP/private-key
blocks (-----BEGIN [A-Z ]*PRIVATE KEY----- with various key names), AWS-style
access keys (AKIA[0-9A-Z]{16}), and common token formats (shorter base64-like or
dot-separated JWTs), keeping appropriate flags (g, i) and using word-boundary or
lookaround anchors similar to existing entries; ensure these new patterns are
applied wherever SECRET_PATTERNS is used before sending content to the model
(e.g., in any redact/clean function) so redaction covers these classes.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a22ffaa and 847e838.

📒 Files selected for processing (1)
  • git-ai/src/services/ConflictResolver.ts

@naheel0 naheel0 merged commit 83b282c into main Mar 3, 2026
7 checks passed
@naheel0 naheel0 deleted the code branch March 3, 2026 18:16
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.

3 participants