Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 64 additions & 17 deletions ai-code-github.el
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
(require 'magit)
(require 'ai-code-input)
(require 'ai-code-prompt-mode)
(require 'which-func nil t)


(declare-function ai-code-read-string "ai-code-input")
(declare-function ai-code--insert-prompt "ai-code-prompt-mode" (prompt-text))
Expand All @@ -24,6 +26,11 @@
(declare-function ai-code--generate-branch-or-commit-diff "ai-code-git" (diff-params diff-file))
(declare-function ai-code--open-diff-file "ai-code-git" (diff-file))
(declare-function ai-code--explain-code-change "ai-code-discussion" (&optional review-source))
(declare-function ai-code--get-context-files-string "ai-code-utils" ())
(declare-function ai-code--format-repo-context-info "ai-code-utils" ())
(declare-function ai-code--get-region-location-info "ai-code-discussion" (region-beginning region-end))
(declare-function which-function "which-func" ())


(defcustom ai-code-default-review-source nil
"Default review source for pull request and issue analysis.
Expand Down Expand Up @@ -167,11 +174,13 @@ Feedback Check Steps:
4. No need to make code change. Provide analysis only."
pr-url source-instruction)))

(defun ai-code--build-issue-investigation-init-prompt (review-source issue-url)
"Build issue investigation prompt for REVIEW-SOURCE with ISSUE-URL."
(let ((source-instruction
(ai-code--pull-or-review-source-instruction review-source 'investigate-issue)))
(format "Investigate issue: %s
(defun ai-code--build-issue-investigation-init-prompt (review-source issue-url &optional include-context)
"Build issue investigation prompt for REVIEW-SOURCE with ISSUE-URL.
If INCLUDE-CONTEXT is non-nil, append current editor context to the prompt."
(let* ((source-instruction
(ai-code--pull-or-review-source-instruction review-source 'investigate-issue))
(prompt
(format "Investigate issue: %s

%s

Expand All @@ -180,7 +189,37 @@ Issue Investigation Steps:
2. Analyze relevant code in this repository as context and identify likely root causes.
3. Provide concrete insights on how to fix it, including likely files or areas to change.
4. No need to make code change. Provide analysis only."
issue-url source-instruction)))
issue-url source-instruction)))
(if (and include-context (or buffer-file-name (use-region-p)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor pinned context outside file buffers

When Investigate issue is invoked from a non-file buffer such as Magit status, pinned repository context can still be available because ai-code--format-repo-context-info keys off default-directory, but this guard returns the base prompt before checking it (and the caller mirrors the same buffer-file-name/region test). As a result C-u/include-context silently drops the stored repo context, which is one of the advertised context sources; build the blocks first or include repo/visible context in the availability test.

Useful? React with 👍 / 👎.

(let* ((region-text (when (use-region-p)
(buffer-substring-no-properties (region-beginning) (region-end))))
(region-location-info (when region-text
(ai-code--get-region-location-info
(region-beginning)
(region-end))))
Comment on lines +197 to +199

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Load discussion helpers before using regions

When ai-code-github is loaded without ai-code-discussion and the user has an active region and accepts context, this call is reached but the helper is only declared, not required, so Emacs signals (void-function ai-code--get-region-location-info) before sending the prompt. The explain-code-change branch requires ai-code-discussion, but the issue-investigation context path needs the same require or an fboundp guard before using the region helper.

Useful? React with 👍 / 👎.

(function-name (which-function))
(files-context-string (ai-code--get-context-files-string))
(repo-context-string (ai-code--format-repo-context-info))
(context-blocks nil))
(push (if buffer-file-name
(format "Current file: %s" buffer-file-name)
(format "Current buffer: %s" (buffer-name)))
context-blocks)
(when function-name
(push (format "Function: %s" function-name) context-blocks))
(when region-text
(push (concat "Selected region:\n"
(when region-location-info
(concat region-location-info "\n"))
region-text)
context-blocks))
(when (and files-context-string (not (string-empty-p files-context-string)))
(push files-context-string context-blocks))
(when (and repo-context-string (not (string-empty-p repo-context-string)))
(push repo-context-string context-blocks))
(concat prompt "\n\nLocal Context:\n"
(mapconcat #'identity (nreverse context-blocks) "\n\n")))
prompt)))

(defun ai-code--build-pr-description-init-prompt (review-source pr-url)
"Build PR description prompt for REVIEW-SOURCE with PR-URL."
Expand Down Expand Up @@ -285,11 +324,12 @@ Signal a helpful error when difftastic is unavailable."
"Create the pull request using the backend's PR creation capability. "
"Do not treat this as a PR review flow before the PR exists."))))

(defun ai-code--build-pr-init-prompt (review-source target-url review-mode)
"Build initial prompt for REVIEW-SOURCE, TARGET-URL and REVIEW-MODE."
(defun ai-code--build-pr-init-prompt (review-source target-url review-mode &optional include-context)
"Build initial prompt for REVIEW-SOURCE, TARGET-URL and REVIEW-MODE.
If INCLUDE-CONTEXT is non-nil, append current editor context to the prompt."
(pcase review-mode
('investigate-issue
(ai-code--build-issue-investigation-init-prompt review-source target-url))
(ai-code--build-issue-investigation-init-prompt review-source target-url include-context))
('check-feedback
(ai-code--build-pr-feedback-check-init-prompt review-source target-url))
('review-ci-checks
Expand All @@ -301,8 +341,9 @@ Signal a helpful error when difftastic is unavailable."
(_
(ai-code--build-pr-review-init-prompt review-source target-url))))

(defun ai-code--pull-or-review-pr-with-source (review-source)
"Prompt for a mode and send a prompt for REVIEW-SOURCE to AI."
(defun ai-code--pull-or-review-pr-with-source (review-source &optional arg)
"Prompt for a mode and send a prompt for REVIEW-SOURCE to AI.
ARG is the optional prefix argument to force including context."
(require 'ai-code-git nil t)
(let* ((review-mode (ai-code--pull-or-review-pr-mode-choice)))
(cond
Expand Down Expand Up @@ -337,8 +378,13 @@ Signal a helpful error when difftastic is unavailable."
review-source current-branch target-branch pr-title)))
(let* ((url-prompt (ai-code--pull-or-review-url-prompt review-mode))
(region-url (ai-code--extract-url-from-region))
(target-url (ai-code-read-string url-prompt region-url)))
(ai-code--build-pr-init-prompt review-source target-url review-mode))))
(target-url (ai-code-read-string url-prompt region-url))
(has-context (or buffer-file-name (use-region-p)))
(include-context (and (eq review-mode 'investigate-issue)
has-context
(or arg
(y-or-n-p "Include active buffer and editor context? ")))))
(ai-code--build-pr-init-prompt review-source target-url review-mode include-context))))
(prompt-label (if (eq review-mode 'send-current-branch-pr)
"Enter PR creation prompt: "
"Enter review prompt: ")))
Expand Down Expand Up @@ -384,11 +430,12 @@ Works with GitHub, GitLab, Bitbucket, and other Git hosting services."
(message "Opened web commit: %s" commit-url))
(message "Unable to determine repository web URL"))))

(defun ai-code-pull-or-review-diff-file ()
(defun ai-code-pull-or-review-diff-file (&optional arg)
"Review a diff file with AI Code or choose a PR review workflow.
If current buffer is a .diff file, ask AI Code to review it.
Otherwise, prompt for a review source and analysis mode."
(interactive)
Otherwise, prompt for a review source and analysis mode.
ARG is the optional prefix argument to force including context."
(interactive "P")
(if (and buffer-file-name (string-match-p "\\.diff$" buffer-file-name))
(let* ((file-name (file-name-nondirectory buffer-file-name))
(init-prompt (format "Code review for %s. Use relevant file in repository as context.
Expand All @@ -406,7 +453,7 @@ Provide overall assessment.
(unless ai-code-default-review-source
(ai-code--message-review-source-config-hint))
(let ((review-source (ai-code--pull-or-review-action-choice)))
(ai-code--pull-or-review-pr-with-source review-source))))
(ai-code--pull-or-review-pr-with-source review-source arg))))

(provide 'ai-code-github)

Expand Down
35 changes: 32 additions & 3 deletions test/test_ai-code-github.el
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Return (CAPTURED-PROMPT DIFF-CALLED)."
(setq completing-read-results (cdr completing-read-results))
selected)))
((symbol-function 'ai-code-read-string)
(lambda (prompt &optional initial-input _candidate-list)
(lambda (prompt &optional initial-input &rest _args)
(if (string-match-p "URL:" prompt)
pr-url
initial-input)))
Expand Down Expand Up @@ -94,7 +94,7 @@ Return (CAPTURED-PROMPT DIFF-CALLED)."
((symbol-function 'ai-code--pull-or-review-action-choice)
(lambda () 'github-mcp))
((symbol-function 'ai-code--pull-or-review-pr-with-source)
(lambda (_review-source)
(lambda (_review-source &rest _args)
nil)))
(with-temp-buffer
(ai-code-pull-or-review-diff-file))
Expand All @@ -108,7 +108,7 @@ Return (CAPTURED-PROMPT DIFF-CALLED)."
(lambda (&rest _args)
(setq message-called t)))
((symbol-function 'ai-code--pull-or-review-pr-with-source)
(lambda (_review-source)
(lambda (_review-source &rest _args)
nil)))
(with-temp-buffer
(ai-code-pull-or-review-diff-file))
Expand Down Expand Up @@ -421,6 +421,35 @@ Return (CAPTURED-PROMPT DIFF-CALLED)."
(should (string-match-p "conflict" (downcase captured-prompt)))
(should-not diff-called)))

(ert-deftest ai-code-test-pull-or-review-diff-file-investigate-issue-with-context ()
"Test that investigate issue mode can include current file context when y-or-n-p is true."
(let* ((captured-prompt nil)
(completing-read-results '("Use GitHub MCP server" "Investigate issue")))
(with-temp-buffer
(setq buffer-file-name "/path/to/my-source-file.el")
(insert "defun hello-world ()")
(cl-letf (((symbol-function 'completing-read)
(lambda (&rest _args)
(let ((selected (car completing-read-results)))
(setq completing-read-results (cdr completing-read-results))
selected)))
((symbol-function 'ai-code-read-string)
(lambda (prompt &optional initial-input &rest _args)
(if (string-match-p "URL:" prompt)
"https://github.com/acme/demo/issues/42"
initial-input)))
((symbol-function 'ai-code--insert-prompt)
(lambda (prompt) (setq captured-prompt prompt)))
((symbol-function 'y-or-n-p)
(lambda (prompt)
(should (string-match-p "Include active buffer and editor context" prompt))
t))
((symbol-function 'ai-code--git-root) (lambda () "/path/to/")))
(ai-code-pull-or-review-diff-file)))
(should (string-match-p "Investigate issue: https://github.com/acme/demo/issues/42" captured-prompt))
(should (string-match-p "Local Context:" captured-prompt))
(should (string-match-p "Current file: /path/to/my-source-file.el" captured-prompt))))

(provide 'test_ai-code-github)

;;; test_ai-code-github.el ends here
Loading