feat(ai-ide): add AI-generated commit message button#17669
Conversation
sdirix
left a comment
There was a problem hiding this comment.
The design of the commit-message-agent is really weird to me:
- It should not be a chat agent because we don't use it in chat
- Because it is a chat agent you need to add all kind of weird workarounds to prevent it from showing up in chat, throwing away the chat model etc.
- I need to manually confirm the tool it will call:
- I already clicked on "Generate message from all changes" so it's unexpected to me that I need to again confirm the tool.
- Because notifications are shown by default in small text on the bottom right, it's easily overlooked
- The notification only allows No and Yes (forever)
For a single tool I would not even give the agent the control over it but just run it and inject the result manually into the prompt. This is how we do it for other non-chat agents.
I can understand giving the agent a bit more tools in case that just the diffs are not enough and it also wants to check past commits, maybe more of the workspace, naming conventions, agent.mds etc. But I think all of these are then implicitly always allowed. Why would I even run the agent if I don't want it to have that information?
| return 'git diff HEAD --no-color; ' | ||
| + 'git ls-files --others --exclude-standard -z ' | ||
| + '| xargs -0 -r -I{} sh -c \'git diff --no-index --no-color -- /dev/null "$1" || true\' _ {}'; |
There was a problem hiding this comment.
The "all changes" command is POSIX-only: ; as separator, git ls-files ... -z | xargs -0, sh -c, and /dev/null don't work in cmd.exe. ShellExecutionServer spawns with shell: true (master), which is cmd.exe on Windows, so this branch breaks there while the staged path is fine. Either build a Windows-aware command or document that "all changes" generation is POSIX-only.
| return choice === replace; | ||
| } | ||
|
|
||
| protected async invokeAndAwaitCompletion(request: MutableChatRequestModel): Promise<void> { |
There was a problem hiding this comment.
AbstractStreamParsingChatAgent.invoke() already resolves only after the response is finished (it awaits onResponseComplete at the end and catches its own errors), so the parallel completion-listener here looks redundant. task-context-service drives an agent the same way with a plain await agent.invoke(...) then reads the response (master). A single await invoke() plus the existing state checks should be enough.
| } | ||
|
|
||
| async run(scope: CommitMessageScope): Promise<void> { | ||
| if (this.running.has(scope)) { |
There was a problem hiding this comment.
run only guards the same scope, so running GENERATE_FROM_STAGED and GENERATE_FROM_ALL from the command palette can start both at once and race on repository.input.value. The widget prevents this by disabling the other button, but the palette path bypasses that. Guarding any in-flight run here would enforce the invariant the buttons rely on.
… with hardcoded commands Introduces a generic abstract `PredefinedShellTool` base class for tools that run a fixed, hardcoded shell command. Subclasses declare a typed parameter schema and a `buildCommand(args)` method that assembles the exact shell command from the typed arguments — the LLM only supplies the typed arguments, so the command itself is fully controlled by the subclass. Unlike the general-purpose `shellExecute` tool, instances of `PredefinedShellTool` do not consult `ShellCommandPermissionService` and do not need to appear on the user shell allow/deny lists: the safety boundary is the subclass-controlled `buildCommand`, which must not be coercible into running arbitrary commands. The tool participates in the regular `ToolConfirmationMode` flow; callers driving an agent without a UI (where the per-call confirmation modal has nowhere to render) are responsible for arranging the appropriate confirmation via `ToolConfirmationManager`. Re-exported from the ai-terminal browser entry point for consumption by other packages.
Adds an AI-powered commit message generator to the SCM commit widget: - `GetGitChangesTool`, a `PredefinedShellTool` exposing the repository git diff (staged or all changes vs HEAD, plus untracked files as addition diffs for the all-changes case) to the agent. - `CommitMessageAgent` and `CommitMessageRunner` that drive a silent agent invocation against a private `MutableChatModel` to produce a commit message from the current changes, without surfacing a chat session in the panel. - `CommitMessageCommandContribution` and command wiring. - `AiAwareScmCommitWidget` rebinding the SCM commit widget to render the generate button, gated on `AIActivationService`, plus the prompt template and styles. - A one-time tool-allow gate in the runner that asks the user to flip `getGitChanges` to `ALWAYS_ALLOW` when it is currently on `CONFIRM` or `DISABLED`, with differentiated dialog copy for each case.
Signed-off-by: Simon Graband <sgraband@eclipsesource.com>
What it does
Adds an AI-powered "generate commit message" button to the SCM commit widget.
The work spans two packages.
@theia/ai-terminalgets a newPredefinedShellToolbase class. It is anabstract
ToolProviderfor tools that run a fixed, hardcoded shell command.Subclasses declare a typed parameter schema and a
buildCommand(args)methodthat assembles the exact command. The LLM only supplies typed arguments, so the
command itself is fully controlled by the subclass. Unlike
shellExecute,instances do not consult
ShellCommandPermissionServiceand do not need toappear on the user's allow/deny lists. The tool itself participates in the
regular
ToolConfirmationModeflow.@theia/ai-ideuses that to add the commit-message generator:GetGitChangesTool(getGitChanges) returns the repository's git diff.With
stagedOnly = trueit runsgit diff --cached. WithstagedOnly = falseit combines
git diff HEADwith the contents of untracked, non-ignored files(rendered as addition diffs via
git diff --no-index) so brand-new filesreach the model. It is scoped to the selected SCM repository's root.
CommitMessageAgentis anAbstractStreamParsingChatAgentwith its ownprompt template. It is intentionally not registered as a
ChatAgent: it doesnot appear in the
@-mention list and is not invokable from the chat panel.CommitMessageRunnerdrives the agent silently against a privateMutableChatModel(no chat-panel session), writes the resulting message intothe SCM commit input, and handles cancellation, the overwrite-confirm prompt,
and a one-time tool-allow gate that asks the user to flip
getGitChangestoALWAYS_ALLOWwhen it is currently onCONFIRMorDISABLED. The dialogcopy differs between the two cases.
AiAwareScmCommitWidgetrebinds the SCM commit widget to overlay two sparklebuttons (staged / all). It is gated on
AIActivationService.isActive, sousers with AI globally disabled keep the stock SCM input with no overlay and
no extra padding. While one scope is generating, the other scope's button is
disabled with a tooltip explaining the situation.
CommitMessageCommandContributionexposes two commands that togglerun / cancel; the buttons fire them.
How to test
Workspace root is a git repo, AI features enabled, working model behind the
default/universalalias, SCM view open.@in the chat view:CommitMessagedoes not show up in the mention list.getGitChangeson the defaultConfirmmode, click a sparkle then pick Cancel: nothing runs, the tool stays on Confirm.Always allow; the next click runs with no dialog.getGitChangestoDisabledand click a sparkle: the dialog text changes to mention the tool is currently disabled, picking Allow flips it back toAlways allowand runs..gitignoreentry for some artefact: its content does not influence the generated message.Breaking changes
Attribution
Review checklist
nlsservice (for details, please see the Internationalization/Localization section in the Coding Guidelines)Reminder for reviewers