Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
e31e8a7
Add Subject and Description automation actions and a strict mode
thykel Oct 12, 2026
114e077
Add the ai_assistance flag, permission and journal cause
thykel Oct 12, 2026
758fe29
Add AI assistant, skill, run and finding models
thykel Oct 12, 2026
36c3c5a
Add the skill registry and the health check tree rules
thykel Oct 12, 2026
18fcca9
Add the health check relation and staleness rules
thykel Oct 12, 2026
d27e52a
Add the health check rules for linked pull requests
thykel Oct 12, 2026
bba4f13
Add the run service, the run controllers and the run dialog
thykel Oct 12, 2026
9a75fd8
Add the AI Assistance menu to the work package toolbars
thykel Oct 12, 2026
4975f52
Add the AI review to the health check
thykel Oct 12, 2026
44fc072
Apply proposed changes from AI findings
thykel Oct 12, 2026
1c95afe
Add admin pages for AI assistants and their skills
thykel Oct 12, 2026
038443d
Document the AI Assistance concept and the review split
thykel Oct 12, 2026
9feb8f1
Add the AI Assistance chat on work packages
thykel Oct 12, 2026
897403a
Add custom prompt presets, a comment-only scope and a copy button
thykel Oct 12, 2026
57783c5
Use "AI review" in the interface
thykel Oct 12, 2026
5f7dc95
Rename the AI judgement to AI review, and move shared proposal classe…
thykel Oct 12, 2026
6cf7eef
Clean up AI Assistance after review
thykel Oct 12, 2026
e3a078f
Show a spinner while the AI review runs
thykel Oct 12, 2026
6943bff
Hide AI Assistance without an LLM, and do not call a partial check co…
thykel Oct 12, 2026
a6c7872
Call the health check "Refresh work package" in the interface
thykel Oct 13, 2026
3e68882
Send the project name and related meeting agendas to the model
thykel Oct 13, 2026
8250225
Document the OpenProject work for a coding agent integration
thykel Oct 13, 2026
61159ef
Fix two AI Assistance specs after the cleanup
thykel Oct 13, 2026
58eda62
Add personal AI assistants in My account
thykel Oct 13, 2026
dd69bc4
Delete AI Assistance records with their user
thykel Oct 13, 2026
462f382
Accept the pull_requests area in the AI review
thykel Oct 13, 2026
e340620
Simplify AI Assistance after the second review
thykel Oct 13, 2026
8971222
Let a group own an AI assistant
thykel Oct 13, 2026
dbcdb94
Seed a sample group assistant in the development data
thykel Oct 13, 2026
2330d7d
Seed one sample assistant for each owner in the development data
thykel Oct 13, 2026
da1bced
Give the sample COMMS assistant to a COMMS team
thykel Oct 13, 2026
b599eda
Trim the seeded AI assistants to one global OP assistant
thykel Oct 13, 2026
070b839
Sort the AI Assistance locale keys for Yamllint
thykel Oct 13, 2026
9f6d426
Offer AI Assistance skills only where their conditions match
thykel Oct 13, 2026
b3a8257
Generate the output rules, and share instructions across an assistant…
thykel Oct 13, 2026
dbb9f7a
Remove the unused template reference of AI Assistance skills
thykel Oct 13, 2026
bc9bf7f
Use the default spacing between the AI Assistance form sections
thykel Oct 13, 2026
7d08846
Show skill conditions in the list, and keep them valid
thykel Oct 13, 2026
a274dbc
Make health check findings stable, and propose changes as proposals
thykel Oct 13, 2026
307f8a3
Give the AI context the accountable person, and widen the history dialog
thykel Oct 14, 2026
c517a52
Describe the split between core skills and agent skills
thykel Oct 14, 2026
bab4eaf
Store the proposals of a conversation answer as their own records
thykel Oct 14, 2026
1ca8664
Replace the AI skill types with one conversation type and settings
thykel Oct 14, 2026
2a07985
Answer with findings in the conversation, and with several proposals
thykel Oct 14, 2026
2929bac
Give AI skills a first message instead of start and answer settings
thykel Oct 14, 2026
a2703bb
Open every AI skill in the conversation, with its proposals as cards
thykel Oct 14, 2026
cdfbc9d
Remove AI runs and the built-in rules, and check consistency with the…
thykel Oct 14, 2026
32bc1ac
Describe AI Assistance as conversations with proposals
thykel Oct 14, 2026
dbbcd42
Keep AI answers short when they carry proposals
thykel Oct 14, 2026
28daceb
Remove leftovers of the AI rules from proposals
thykel Oct 14, 2026
69e8a53
Merge remote-tracking branch 'origin/dev' into ai-assistance/health-c…
thykel Oct 14, 2026
a1a679a
Resolve the AI Assistance model through Llm::Runtime
thykel Oct 14, 2026
1d6f867
Squash the AI Assistance migrations into one
thykel Oct 14, 2026
27fcaad
Start AI skills with a start line instead of their label
thykel Oct 14, 2026
e55f048
Remove leftovers of the AI skill label as first message
thykel Oct 14, 2026
2f10b3f
Add an open filter and a count tool to the MCP work package tools
thykel Oct 15, 2026
a89c1bd
Let AI Assistance skills call the read-only MCP tools
thykel Oct 15, 2026
2c0c916
Show AI tool calls while the answer loads, and stop repeated calls
thykel Oct 15, 2026
0717aaf
Document the MCP tools of AI Assistance
thykel Oct 15, 2026
6c99d32
Return the values of list custom fields from the MCP items tool
thykel Oct 15, 2026
7db563e
Tell the AI not to call a tool again to check a result
thykel Oct 15, 2026
48007f1
Let an AI skill opt out of the MCP tools
thykel Oct 15, 2026
17940c0
Fix the ERB layout of the AI skill form
thykel Oct 15, 2026
1a6fa5a
Show the typed AI skill prompt in the full prompt preview, and rename…
thykel Oct 15, 2026
578d590
Move the AI Assistance button into the app header
thykel Oct 15, 2026
0e87f3a
Link AI journal entries to their chat
thykel Oct 15, 2026
3b16049
Group the AI Assistance menu by assistant
thykel Oct 15, 2026
982a2b4
Give each AI skill an icon, and drop the assistant icon
thykel Oct 15, 2026
9c28369
Separate History from the skills in the AI Assistance menu
thykel Oct 15, 2026
8d22432
Do not start a second AI answer while one is running
thykel Oct 15, 2026
23a6f75
Offer AI Assistance on every page
thykel Oct 15, 2026
7783fb9
Show AI assistants as sections with their skills
thykel Oct 15, 2026
48aa972
Link AI assistant owners to their public profiles
thykel Oct 15, 2026
d018cc2
Copy AI assistants and skills
thykel Oct 15, 2026
b2beab1
Rework the AI Assistance seed data
thykel Oct 15, 2026
0e67caa
Rename the seeded OP assistant to OPAssistant
thykel Oct 15, 2026
6c99add
Count work per person, and seed the COMMS developers' workloads
thykel Oct 15, 2026
7b0b409
Show the owner icon of each assistant in the AI Assistance menu
thykel Oct 15, 2026
3ca7731
Log AI tool results and the model's text, and stop repeats early
thykel Oct 15, 2026
af809f1
Put the owner icon after the assistant name in the AI Assistance menu
thykel Oct 15, 2026
924c090
Log the model's reasoning, and answer now after a repeated tool call
thykel Oct 15, 2026
f15dbe9
Dim the owner icon in the AI Assistance menu headings
thykel Oct 15, 2026
42e3cec
Dim the owner icon with opacity
thykel Oct 15, 2026
e8f316e
Ask AI answers to name people, not guess pronouns
thykel Oct 15, 2026
52f389f
Merge remote-tracking branch 'origin/dev' into ai-assistance/health-c…
thykel Oct 15, 2026
8f294ad
Seed the AI Assistance demo on every fresh install of this branch
thykel Oct 15, 2026
dbf948c
Merge remote-tracking branch 'origin/dev' into ai_assistant_demo_2
thykel Oct 16, 2026
9081ea0
Merge remote-tracking branch 'origin/dev' into ai-assistance/health-c…
thykel Oct 18, 2026
7e0bd77
rename a clashing migration
thykel Oct 18, 2026
8cbd090
Keep AI findings that other models label differently
thykel Oct 18, 2026
5f7d778
Merge branch 'ai-assistance/health-check' into ai_assistant_demo_2
thykel Oct 18, 2026
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
1 change: 1 addition & 0 deletions .github/workflows/pullpreview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
echo "OPENPROJECT_FEATURE__SHOW__CHANGES__ACTIVE=true" >> .env.pullpreview
echo "OPENPROJECT_FEATURE__AI__TEXT__TRANSFORM__ACTIONS__ACTIVE=true" >> .env.pullpreview
echo "OPENPROJECT_FEATURE__LLM__CONNECTION__ACTIVE=true" >> .env.pullpreview
echo "OPENPROJECT_FEATURE__AI__ASSISTANCE__ACTIVE=true" >> .env.pullpreview
echo "OPENPROJECT_FEATURE__SEMANTIC__SEARCH__ACTIVE=false" >> .env.pullpreview
echo "OPENPROJECT_LOOKBOOK__ENABLED=true" >> .env.pullpreview
echo "OPENPROJECT_HSTS=false" >> .env.pullpreview
Expand Down
94 changes: 94 additions & 0 deletions app/components/ai/assistance/chat_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<%#
-- copyright
OpenProject is an open source project management software.
Copyright (C) the OpenProject GmbH

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.

OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

See COPYRIGHT and LICENSE files for more details.

++#
%>

<%# position: relative keeps absolutely positioned form labels inside the scrolling body;
without it they stretch the dialog and give it a second scrollbar. %>
<%= component_wrapper(data: wrapper_data, class: "position-relative") do %>
<% if messages.empty? %>
<%= render(Primer::Beta::Blankslate.new(data: { test_selector: "ai-assistance-chat-empty" })) do |slate| %>
<% slate.with_visual_icon(icon: :sparkle) %>
<% slate.with_heading(tag: :h3).with_content(empty_title) %>
<% slate.with_description { empty_description } %>
<% end %>
<% end %>

<% messages.each_with_index do |message, index| %>
<% last = index == messages.size - 1 %>
<% if message.from_start? %>
<%= render(Primer::Box.new(display: :flex, align_items: :center, my: 3, data: { test_selector: "ai-assistance-chat-start" })) do %>
<%= render(Primer::Box.new(flex: 1, border: :top)) %>
<%= render(Primer::Beta::Text.new(color: :muted, font_size: :small, text_align: :center, px: 2)) do %>
<%= start_text %><br>
<%= helpers.format_time(message.created_at) %>
<% end %>
<%= render(Primer::Box.new(flex: 1, border: :top)) %>
<% end %>
<% elsif message.from_user? %>
<%= render(Primer::Box.new(display: :flex, justify_content: :flex_end, mb: 2)) do %>
<%= render(Primer::Box.new(bg: :accent, border_radius: 2, p: 2, style: "max-width: 85%; white-space: pre-wrap;", data: { test_selector: "ai-assistance-chat-question" })) { message.content } %>
<% end %>
<% else %>
<%= render(Primer::Box.new(mb: 3, data: { test_selector: "ai-assistance-chat-answer", controller: (last ? "scroll-into-view" : nil) })) do %>
<% if message.failed? %>
<%= render(Primer::Alpha::Banner.new(scheme: :danger)) { t("ai_assistance.chat.failed") } %>
<% elsif message.done? && message.content.present? %>
<div class="op-uc-container"><%= helpers.format_text(message.content) %></div>
<%= render(Primer::Beta::ClipboardCopyButton.new(id: "ai-assistance-copy-#{message.id}", value: message.content,

Check failure on line 64 in app/components/ai/assistance/chat_component.html.erb

View workflow job for this annotation

GitHub Actions / ERB lint

[erb_lint] reported by reviewdog 🐶 Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list. Raw Output: app/components/ai/assistance/chat_component.html.erb:64:59: Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list.

Check failure on line 64 in app/components/ai/assistance/chat_component.html.erb

View workflow job for this annotation

GitHub Actions / ERB lint

[erb_lint] reported by reviewdog 🐶 Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list. Raw Output: app/components/ai/assistance/chat_component.html.erb:64:21: Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list.
aria: { label: t("ai_assistance.chat.copy") },
scheme: :invisible, size: :small,
data: { test_selector: "ai-assistance-chat-copy" })) %>
<% end %>
<% if message.proposals.any? %>
<%= render(Primer::Beta::BorderBox.new(mt: 2, data: { test_selector: "ai-assistance-proposals" })) do |box| %>
<% message.proposals.each do |proposal| %>
<% box.with_row { render(AI::Assistance::ProposalItemComponent.new(proposal:, read_only:)) } %>
<% end %>
<% end %>
<% end %>
<% if message.pending? %>
<%= render(Primer::Beta::Text.new(tag: :p, color: :muted, mt: 2)) do %>
<%= render(Primer::Beta::Spinner.new(size: :small, mr: 1)) %><%= pending_text(index.positive? ? messages[index - 1] : nil) %>
<% end %>
<% steps = tool_steps(message) %>
<% if steps.any? %>
<ul class="list-style-none pl-4 mt-1" data-test-selector="ai-assistance-tool-steps">
<% steps.each do |step| %>
<li><%= render(Primer::Beta::Text.new(color: :muted, font_size: :small)) { step } %></li>
<% end %>
</ul>
<% end %>
<% end %>
<%= render(AI::Assistance::RunEventsComponent.new(events: message.events)) %>
<% end %>
<% end %>
<% end %>

<% end %>
115 changes: 115 additions & 0 deletions app/components/ai/assistance/chat_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module AI
module Assistance
# The conversation in the dialog body. Replaced after each question, and polled while
# an answer is pending.
class ChatComponent < ApplicationComponent
include OpTurbo::Streamable
include Polling

attr_reader :place, :skill, :chat, :read_only

def initialize(place:, skill:, chat:, read_only: false)
super
@place = place
@skill = skill
@chat = chat
@read_only = read_only
end

def self.wrapper_key = "ai-assistance-chat"

# Changes with each event of a pending answer, so a poll can show the tool calls so far.
def self.progress_reference(chat)
chat.messages.select(&:pending?).sum { |message| message.events.size }.to_s
end

def messages
chat ? chat.messages.includes(:proposals).to_a : []
end

def pending?
chat&.pending?
end

def wrapper_data
return {} unless pending?

poll_data(url_helpers.poll_ai_assistance_chat_path(chat_id: chat.id))
.merge(poll_for_changes_reference_value: self.class.progress_reference(chat))
end

# Off a work package, a skill proposes nothing.
def read_only?
skill.scope_read_only? || !place.work_package?
end

def empty_title
place.work_package? ? I18n.t("ai_assistance.chat.empty_title") : I18n.t("ai_assistance.chat.empty_title_generic")
end

def empty_description
if place.work_package?
I18n.t("ai_assistance.chat.#{read_only? ? 'empty_description_read_only' : 'empty_description'}")
elsif place.project
I18n.t("ai_assistance.chat.empty_description_project", project: place.project.name)
else
I18n.t("ai_assistance.chat.empty_description_generic")
end
end

def start_text
I18n.t("ai_assistance.chat.started", user: chat.user.name, skill: skill.display_label)
end

# The tool calls of a pending answer, for example "Count work packages (project_id: 1, open: true)".
def tool_steps(message)
message.events.select { |event| event["kind"] == "tool_call" }.map do |event|
data = event["data"]
arguments = (data["arguments"] || {}).map { |key, value| "#{key}: #{value}" }.join(", ")
I18n.t("ai_assistance.chat.tool_step", tool: tool_title(data["name"]), arguments:)
end
end

def pending_text(previous)
key = previous&.from_start? && place.label ? "working_on" : "answering"
I18n.t("ai_assistance.chat.#{key}", assistant: skill.assistant.name, id: place.label)
end

private

def tool_title(name)
McpTools.all.find { |tool| tool.name == name }&.default_title || name
end
end
end
end
41 changes: 41 additions & 0 deletions app/components/ai/assistance/chat_dialog_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<%#
-- copyright
OpenProject is an open source project management software.
Copyright (C) the OpenProject GmbH

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.

OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

See COPYRIGHT and LICENSE files for more details.

++#
%>

<%= render(Primer::Alpha::Dialog.new(id: DIALOG_ID, title:, size: :xlarge,

Check failure on line 32 in app/components/ai/assistance/chat_dialog_component.html.erb

View workflow job for this annotation

GitHub Actions / ERB lint

[erb_lint] reported by reviewdog 🐶 Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list. Raw Output: app/components/ai/assistance/chat_dialog_component.html.erb:32:37: Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list.

Check failure on line 32 in app/components/ai/assistance/chat_dialog_component.html.erb

View workflow job for this annotation

GitHub Actions / ERB lint

[erb_lint] reported by reviewdog 🐶 Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list. Raw Output: app/components/ai/assistance/chat_dialog_component.html.erb:32:11: Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list.
data: { test_selector: "ai-assistance-chat-dialog", "keep-open-on-submit": true })) do |dialog| %>
<% dialog.with_header(variant: :large) %>
<% dialog.with_body do %>
<%= render(AI::Assistance::ChatComponent.new(place:, skill:, chat:, read_only:)) %>
<% end %>
<% dialog.with_footer(show_divider: true) do %>
<%= render(AI::Assistance::ChatFormComponent.new(place:, skill:, chat:, read_only:, dialog_id: DIALOG_ID)) %>
<% end %>
<% end %>
51 changes: 51 additions & 0 deletions app/components/ai/assistance/chat_dialog_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module AI
module Assistance
class ChatDialogComponent < ApplicationComponent
include OpTurbo::Streamable

DIALOG_ID = "ai-assistance-chat-dialog"

attr_reader :place, :skill, :chat, :read_only

def initialize(place:, skill:, chat:, read_only: false)
super
@place = place
@skill = skill
@chat = chat
@read_only = read_only
end

def title = skill.full_label
end
end
end
69 changes: 69 additions & 0 deletions app/components/ai/assistance/chat_form_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<%#
-- copyright
OpenProject is an open source project management software.
Copyright (C) the OpenProject GmbH

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.

OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

See COPYRIGHT and LICENSE files for more details.

++#
%>

<%= component_wrapper(class: "width-full") do %>
<% if read_only %>
<%= render(Primer::Box.new(display: :flex, align_items: :center, data: { test_selector: "ai-assistance-chat-read-only" })) do %>
<%= render(Primer::Beta::Text.new(color: :muted, font_size: :small, flex: 1)) { t("ai_assistance.chat.not_offered") } %>
<%= render(Primer::Beta::Button.new(type: :button, ml: 2, data: { close_dialog_id: dialog_id })) { t(:button_close) } %>
<% end %>
<% else %>
<%= primer_form_with(url: send_path, method: :post, html: { class: "mb-0" }, data: { test_selector: "ai-assistance-chat-form" }) do %>
<%= hidden_field_tag :skill_id, skill.id %>
<% if chat %>
<%= hidden_field_tag :chat_id, chat.id %>
<% end %>
<%= render(Primer::Alpha::TextArea.new(name: :content, label: t("ai_assistance.chat.question"), visually_hide_label: true,

Check failure on line 44 in app/components/ai/assistance/chat_form_component.html.erb

View workflow job for this annotation

GitHub Actions / ERB lint

[erb_lint] reported by reviewdog 🐶 Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list. Raw Output: app/components/ai/assistance/chat_form_component.html.erb:44:45: Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list.

Check failure on line 44 in app/components/ai/assistance/chat_form_component.html.erb

View workflow job for this annotation

GitHub Actions / ERB lint

[erb_lint] reported by reviewdog 🐶 Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list. Raw Output: app/components/ai/assistance/chat_form_component.html.erb:44:17: Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list.
placeholder:,
rows: 2, full_width: true, disabled: pending?)) %>
<%= render(Primer::Box.new(display: :flex, align_items: :center, mt: 2)) do %>
<%= render(Primer::Beta::Button.new(type: :submit, scheme: :invisible, formaction: new_chat_path, formnovalidate: true,

Check failure on line 48 in app/components/ai/assistance/chat_form_component.html.erb

View workflow job for this annotation

GitHub Actions / ERB lint

[erb_lint] reported by reviewdog 🐶 Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list. Raw Output: app/components/ai/assistance/chat_form_component.html.erb:48:44: Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list.

Check failure on line 48 in app/components/ai/assistance/chat_form_component.html.erb

View workflow job for this annotation

GitHub Actions / ERB lint

[erb_lint] reported by reviewdog 🐶 Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list. Raw Output: app/components/ai/assistance/chat_form_component.html.erb:48:19: Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list.
data: { test_selector: "ai-assistance-new-chat" })) do |button| %>
<% button.with_leading_visual_icon(icon: :plus) %>
<%= t("ai_assistance.chat.new") %>
<% end %>
<% if rerun? %>
<%= render(Primer::Beta::Button.new(type: :submit, scheme: :invisible, formaction: rerun_path, formnovalidate: true,

Check failure on line 54 in app/components/ai/assistance/chat_form_component.html.erb

View workflow job for this annotation

GitHub Actions / ERB lint

[erb_lint] reported by reviewdog 🐶 Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list. Raw Output: app/components/ai/assistance/chat_form_component.html.erb:54:21: Layout/FirstMethodArgumentLineBreak: Add a line break before the first argument of a multi-line method argument list.
disabled: pending?, data: { test_selector: "ai-assistance-rerun" })) do |button| %>
<% button.with_leading_visual_icon(icon: :sync) %>
<%= t("ai_assistance.chat.rerun") %>
<% end %>
<% end %>
<%= render(Primer::Box.new(flex: 1)) %>
<%= render(Primer::Beta::Button.new(type: :button, mr: 2, data: { close_dialog_id: dialog_id })) { t(:button_close) } %>
<%= render(Primer::Beta::Button.new(type: :submit, scheme: :primary, disabled: pending?, data: { test_selector: "ai-assistance-chat-send" })) do |button| %>
<% button.with_leading_visual_icon(icon: :"paper-airplane") %>
<%= t("ai_assistance.chat.send") %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
Loading
Loading