x_aeon_agents 1.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/AGENTS.md +136 -0
  3. data/CHANGELOG.md +398 -0
  4. data/README.md +579 -0
  5. data/TODO.md +48 -0
  6. data/bin/xaa +5 -0
  7. data/lib/x_aeon_agents/agent_defaults.rb +60 -0
  8. data/lib/x_aeon_agents/agent_options.rb +58 -0
  9. data/lib/x_aeon_agents/agents/coder_agent.rb +82 -0
  10. data/lib/x_aeon_agents/agents/committer_agent.rb +71 -0
  11. data/lib/x_aeon_agents/agents/developer_agent.rb +235 -0
  12. data/lib/x_aeon_agents/agents/diff_interpreter_agent.rb +80 -0
  13. data/lib/x_aeon_agents/agents/documenter_agent.rb +44 -0
  14. data/lib/x_aeon_agents/agents/executor_agent.rb +9 -0
  15. data/lib/x_aeon_agents/agents/feedback_analyst_agent.rb +62 -0
  16. data/lib/x_aeon_agents/agents/gh_comments.gql +64 -0
  17. data/lib/x_aeon_agents/agents/git_diff_interpreter_agent.rb +57 -0
  18. data/lib/x_aeon_agents/agents/issue_implementer_agent.rb +88 -0
  19. data/lib/x_aeon_agents/agents/one_line_code_diff_summarizer_agent.rb +57 -0
  20. data/lib/x_aeon_agents/agents/plan_generator_agent.rb +51 -0
  21. data/lib/x_aeon_agents/agents/planner_agent.rb +89 -0
  22. data/lib/x_aeon_agents/agents/pull_request_creator_agent.rb +144 -0
  23. data/lib/x_aeon_agents/agents/readme/about_analyzer_agent.rb +55 -0
  24. data/lib/x_aeon_agents/agents/readme/contributing_agent.rb +47 -0
  25. data/lib/x_aeon_agents/agents/readme/development_agent.rb +54 -0
  26. data/lib/x_aeon_agents/agents/readme/documentation_agent.rb +43 -0
  27. data/lib/x_aeon_agents/agents/readme/features_agent.rb +45 -0
  28. data/lib/x_aeon_agents/agents/readme/how_it_works_agent.rb +47 -0
  29. data/lib/x_aeon_agents/agents/readme/license_agent.rb +43 -0
  30. data/lib/x_aeon_agents/agents/readme/public_api_agent.rb +44 -0
  31. data/lib/x_aeon_agents/agents/readme/quick_start_agent.rb +50 -0
  32. data/lib/x_aeon_agents/agents/readme/requirements_agent.rb +43 -0
  33. data/lib/x_aeon_agents/agents/readme_generator_agent.rb +438 -0
  34. data/lib/x_aeon_agents/agents/review_resolver_agent.rb +236 -0
  35. data/lib/x_aeon_agents/agents/review_responder_agent.rb +64 -0
  36. data/lib/x_aeon_agents/agents/skill_generator_agent.rb +95 -0
  37. data/lib/x_aeon_agents/agents/skill_installer_agent.rb +103 -0
  38. data/lib/x_aeon_agents/agents/task_starter_agent.rb +66 -0
  39. data/lib/x_aeon_agents/agents/tester_agent.rb +44 -0
  40. data/lib/x_aeon_agents/cli.rb +384 -0
  41. data/lib/x_aeon_agents/config.rb +110 -0
  42. data/lib/x_aeon_agents/gen_helpers.rb +270 -0
  43. data/lib/x_aeon_agents/helpers.rb +211 -0
  44. data/lib/x_aeon_agents/logger.rb +21 -0
  45. data/lib/x_aeon_agents/providers/cline.rb +78 -0
  46. data/lib/x_aeon_agents/version.rb +6 -0
  47. data/lib/x_aeon_agents.rb +38 -0
  48. metadata +296 -0
@@ -0,0 +1,62 @@
1
+ module XAeonAgents
2
+ module Agents
3
+ # Agent responsible for extracting requirements from PR comments directed at X-Aeon Agents
4
+ class FeedbackAnalystAgent < ComposableAgents::Cline::Agent
5
+ prepend AgentDefaults
6
+
7
+ # Define input artifacts contracts
8
+ #
9
+ # @return [Hash{Symbol => Object}] Set of input artifacts description, per artifact name
10
+ def input_artifacts_contracts
11
+ super.merge(
12
+ pr_description: 'The Pull Request description (context)',
13
+ pr_files_diffs: 'The files modifications that were done in this Pull Request (context)',
14
+ conversations: {
15
+ description: 'All Pull Request conversations and comments to be considered (context)',
16
+ type: :json
17
+ },
18
+ open_comments_to_agents: {
19
+ description: 'The exact list of agent-directed comments that need to be addressed',
20
+ type: :json
21
+ }
22
+ )
23
+ end
24
+
25
+ # Define output artifacts contracts
26
+ #
27
+ # @return [Hash{Symbol => Object}] Set of output artifacts description, per artifact name
28
+ def output_artifacts_contracts
29
+ super.merge(
30
+ requirements: {
31
+ description: 'The requirements that will implement what is needed by the agent-directed comments ' \
32
+ '(reply "No requirements" if there is no implementation needed)',
33
+ type: :markdown
34
+ }
35
+ )
36
+ end
37
+
38
+ # Constructor
39
+ #
40
+ # @param agent_params [Hash{Symbol => Object}] Extra agent parameters
41
+ def initialize(**agent_params)
42
+ super(
43
+ name: 'FeedbackAnalyst',
44
+ role: 'You are a feedback analyst agent, analyzing feedback from a Pull Request and devising new requirements to address this feedback.',
45
+ objective: 'Extract requirements from Pull Request comments',
46
+ constraints: <<~EO_CONSTRAINTS,
47
+ - You are in read-only mode.
48
+ - Do NOT modify or write any file.
49
+ - Focus only on agent-directed comments (/agent) for requirement extraction.
50
+ - Output clear, actionable requirements or "No requirements" if none exist.
51
+ EO_CONSTRAINTS
52
+ skills: %w[
53
+ applying-ruby-conventions
54
+ applying-test-conventions
55
+ enforcing-project-rules
56
+ ],
57
+ **agent_params
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,64 @@
1
+ # Schema: https://raw.githubusercontent.com/octokit/graphql-schema/master/schema.graphql
2
+
3
+ query FetchReviewComments($owner: String!, $repo: String!, $pr: Int!) {
4
+ repository(owner: $owner, name: $repo) {
5
+ pullRequest(number: $pr) {
6
+ url
7
+ reviewDecision
8
+ reviewThreads(first: 100) {
9
+ edges {
10
+ node {
11
+ isResolved
12
+ comments(first: 100) {
13
+ totalCount
14
+ nodes {
15
+ databaseId
16
+ pullRequestReview {
17
+ createdAt
18
+ author {
19
+ login
20
+ }
21
+ body
22
+ commit {
23
+ oid
24
+ message
25
+ }
26
+ }
27
+ url
28
+ createdAt
29
+ state
30
+ replyTo {
31
+ databaseId
32
+ createdAt
33
+ author {
34
+ login
35
+ }
36
+ body
37
+ }
38
+ author {
39
+ login
40
+ }
41
+ body
42
+ subjectType
43
+ path
44
+ commit {
45
+ oid
46
+ message
47
+ }
48
+ line
49
+ startLine
50
+ originalCommit {
51
+ oid
52
+ message
53
+ }
54
+ originalLine
55
+ originalStartLine
56
+ diffHunk
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,57 @@
1
+ module XAeonAgents
2
+ module Agents
3
+ # Agent responsible for analyzing git differences with a given git ref base.
4
+ # The git ref base is given in the git_ref_base input artifact.
5
+ # For the staging area diff, use cached as the git_ref_base content.
6
+ class GitDiffInterpreterAgent < ComposableAgents::Agent
7
+ prepend AgentDefaults
8
+
9
+ # Define input artifacts contracts
10
+ #
11
+ # @return [Hash{Symbol => Object}] Set of input artifacts description, per artifact name
12
+ def input_artifacts_contracts
13
+ { git_ref_base: 'Git reference used to diff with' }
14
+ end
15
+
16
+ # Define output artifacts contracts
17
+ #
18
+ # @return [Hash{Symbol => Object}] Set of output artifacts description, per artifact name
19
+ def output_artifacts_contracts
20
+ {
21
+ change_intent: 'Full description of the code changes, their meaning and intent',
22
+ one_line_summary: '1-line summary of the code change intent'
23
+ }
24
+ end
25
+
26
+ # Constructor
27
+ #
28
+ # @param agent_params [Hash{Symbol => Object}] Extra agent parameters
29
+ def initialize(**agent_params)
30
+ super(name: 'Git Diff Interpreter', **agent_params)
31
+ end
32
+
33
+ # Execute the agent to generate some output artifacts based on some input artifacts.
34
+ #
35
+ # @param git_ref_base [String] The git reference to diff with. Use 'cached' for the staging area.
36
+ # @return [Hash{Symbol => Object}] Output artifacts content
37
+ def run(git_ref_base:)
38
+ step_agent(
39
+ diff_interpreter_agent,
40
+ files_diff: Helpers.artifact_files_diffs(git_ref_base == 'cached' ? :cached : git_ref_base)
41
+ )
42
+ step_agent(new_agent(OneLineCodeDiffSummarizerAgent, **Config.agent_options['free_simple']))
43
+ {
44
+ change_intent: @artifacts[:change_intent],
45
+ one_line_summary: @artifacts[:one_line_summary]
46
+ }
47
+ end
48
+
49
+ # Get a Diff Interpreter agent.
50
+ #
51
+ # @return [Agent] The Diff Interpreter agent
52
+ def diff_interpreter_agent
53
+ @diff_interpreter_agent ||= new_agent(DiffInterpreterAgent, **Config.agent_options['free_simple'])
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,88 @@
1
+ module XAeonAgents
2
+ module Agents
3
+ # Agent responsible for implementing a GitHub issue using AI.
4
+ class IssueImplementerAgent < ComposableAgents::Agent
5
+ prepend AgentDefaults
6
+
7
+ # Define input artifacts contracts
8
+ #
9
+ # @return [Hash{Symbol => Object}] Set of input artifacts description, per artifact name
10
+ def input_artifacts_contracts
11
+ { github_issue_number: 'GitHub issue number to implement' }
12
+ end
13
+
14
+ # Constructor
15
+ #
16
+ # @param commit [Boolean] Whether to commit changes automatically
17
+ # @param pull_request [Boolean] Whether to create a pull request automatically
18
+ # @param agent_params [Hash{Symbol => Object}] Extra agent parameters
19
+ def initialize(commit: false, pull_request: false, **agent_params)
20
+ super(name: 'Issue Implementer', **agent_params)
21
+ @commit = commit
22
+ @pull_request = pull_request
23
+ end
24
+
25
+ # Execute the agent to implement a GitHub issue.
26
+ #
27
+ # @param github_issue_number [Integer] The GitHub issue number
28
+ # @return [Hash{Symbol => Object}] Output artifacts content
29
+ def run(github_issue_number:)
30
+ raise 'Unable to find the Github repository' unless Helpers.github_repo
31
+
32
+ issue = Helpers.github.issue(Helpers.github_repo, github_issue_number)
33
+ issue_comments = Helpers.github.issue_comments(Helpers.github_repo, github_issue_number)
34
+ sections = [
35
+ <<~EO_SECTION
36
+ # #{issue.title}
37
+
38
+ #{ComposableAgents::Utils::Markdown.align_markdown_headers(issue.body, level: 2)}
39
+ EO_SECTION
40
+ ]
41
+ unless issue_comments.empty?
42
+ sections << <<~EO_SECTION
43
+ # Comments
44
+
45
+ This is the conversation log that happened in this issue.
46
+ This is provided as a reference to better understand the requirements.
47
+
48
+ #{format_comments_for_artifact(issue_comments)}
49
+ EO_SECTION
50
+ end
51
+ issue_properties = ["Number: #{issue.number}"]
52
+ issue_properties << "Labels: #{issue.labels.map(&:name).join(', ')}" unless issue.labels.empty?
53
+ issue_properties.push(
54
+ "State: #{issue.state}",
55
+ "URL: #{issue.html_url}"
56
+ )
57
+ sections << <<~EO_SECTION
58
+ # Associated Github issue
59
+
60
+ #{issue_properties.map { |line| "- #{line}" }.join("\n")}
61
+ EO_SECTION
62
+ step_agent(
63
+ new_agent(DeveloperAgent, commit: @commit, pull_request: @pull_request),
64
+ requirements: sections.map(&:strip).join("\n\n")
65
+ )
66
+ {}
67
+ end
68
+
69
+ private
70
+
71
+ # Format issue comments for use in artifacts.
72
+ #
73
+ # @param comments [Array<Octokit::IssueComment>] Comments to format
74
+ # @return [String] Formatted comments as markdown
75
+ def format_comments_for_artifact(comments)
76
+ return 'No comments' if comments.empty?
77
+
78
+ comments.sort_by(&:created_at).map do |comment|
79
+ <<~EO_COMMENT
80
+ ## #{comment.user.login} at #{comment.created_at.utc.strftime('%F %T UTC')}
81
+
82
+ #{ComposableAgents::Utils::Markdown.align_markdown_headers(comment.body, level: 3)}
83
+ EO_COMMENT
84
+ end.join("\n")
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,57 @@
1
+ module XAeonAgents
2
+ module Agents
3
+ # Agent responsible for summarizing code change intent into a single line
4
+ class OneLineCodeDiffSummarizerAgent < ComposableAgents::AiAgents::Agent
5
+ prepend AgentDefaults
6
+
7
+ # Define input artifacts contracts
8
+ #
9
+ # @return [Hash{Symbol => Object}] Set of input artifacts description, per artifact name
10
+ def input_artifacts_contracts
11
+ super.merge(change_intent: 'Full description of the code changes, their meaning and intent')
12
+ end
13
+
14
+ # Define output artifacts contracts
15
+ #
16
+ # @return [Hash{Symbol => Object}] Set of output artifacts description, per artifact name
17
+ def output_artifacts_contracts
18
+ super.merge(one_line_summary: '1-line summary of the code change intent')
19
+ end
20
+
21
+ # Constructor
22
+ #
23
+ # @param agent_params [Hash{Symbol => Object}] Extra agent parameters
24
+ def initialize(**agent_params)
25
+ super(
26
+ name: '1-line code diff summarizer',
27
+ role: 'You are a 1-line code diff summarizer agent',
28
+ objective: 'Produce a 1-line summary of a code change intent report.',
29
+ constraints: <<~EO_CONSTRAINTS,
30
+ - You are in read-only mode.
31
+ - Do NOT modify or write any file.
32
+ - You already have ALL the information required.
33
+ - The user's intent is fully specified.
34
+ - You MUST NOT ask follow-up questions.
35
+ EO_CONSTRAINTS
36
+ **agent_params
37
+ )
38
+ self.system_instructions = {
39
+ ordered_list: [
40
+ <<~EO_INSTRUCTIONS,
41
+ Read the artifact named `#{artifact_ref(:change_intent)}` to understand what you need to summarize
42
+
43
+ - This artifact contains already all the information you need to summarize.
44
+ - You don't need to gather more information thatn this.
45
+ EO_INSTRUCTIONS
46
+ <<~EO_INSTRUCTIONS
47
+ Create an artifact named `#{artifact_ref(:one_line_summary)}` to provide a 1-line summary of the code change intent described in the artifact named `#{artifact_ref(:change_intent)}`
48
+
49
+ - Summarize the change intent the same way you would write a git commit comment title.
50
+ - Follow standard git commit title conventions using `feat`, `fix`, etc... with impacted component names.
51
+ EO_INSTRUCTIONS
52
+ ]
53
+ }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,51 @@
1
+ module XAeonAgents
2
+ module Agents
3
+ # Agent responsible for producing detailed implementation plans from requirements
4
+ class PlanGeneratorAgent < ComposableAgents::Cline::Agent
5
+ prepend AgentDefaults
6
+
7
+ # Define input artifacts contracts
8
+ #
9
+ # @return [Hash{Symbol => Object}] Set of input artifacts description, per artifact name
10
+ def input_artifacts_contracts
11
+ super.merge(requirements: 'The initial requirements for which you need to devise an implementation plan')
12
+ end
13
+
14
+ # Define output artifacts contracts
15
+ #
16
+ # @return [Hash{Symbol => Object}] Set of output artifacts description, per artifact name
17
+ def output_artifacts_contracts
18
+ super.merge(
19
+ plan: {
20
+ description: 'The full and detailed implementation plan in Markdown format, ' \
21
+ "that should implement the requirements given by the artifact named `#{artifact_ref(:requirements)}`",
22
+ type: :markdown
23
+ }
24
+ )
25
+ end
26
+
27
+ # Constructor
28
+ #
29
+ # @param agent_params [Hash{Symbol => Object}] Extra agent parameters
30
+ def initialize(**agent_params)
31
+ super(
32
+ name: 'Planner',
33
+ role: 'You are a Planner agent',
34
+ objective: 'Produce a full and detailed implementation plan that can be used to implement some requirements.',
35
+ constraints: <<~EO_CONSTRAINTS,
36
+ - You are in read-only mode.
37
+ - Do NOT modify or write any file.
38
+ - You may only analyze and propose plans.
39
+ - Do NOT execute the plan yourself.
40
+ EO_CONSTRAINTS
41
+ skills: %w[
42
+ applying-ruby-conventions
43
+ applying-test-conventions
44
+ enforcing-project-rules
45
+ ],
46
+ **agent_params
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,89 @@
1
+ module XAeonAgents
2
+ module Agents
3
+ # Agent responsible for producing an implementation plan acceptable to the user.
4
+ class PlannerAgent < ComposableAgents::Agent
5
+ prepend AgentDefaults
6
+
7
+ # Define input artifacts contracts
8
+ #
9
+ # @return [Hash{Symbol => Object}] Set of input artifacts description, per artifact name
10
+ def input_artifacts_contracts
11
+ { requirements: 'The initial requirements for which you need to devise an implementation plan' }
12
+ end
13
+
14
+ # Define output artifacts contracts
15
+ #
16
+ # @return [Hash{Symbol => Object}] Set of output artifacts description, per artifact name
17
+ def output_artifacts_contracts
18
+ {
19
+ plan: {
20
+ description: 'The full and detailed implementation plan in Markdown format, ' \
21
+ "that should implement the requirements given by the artifact named `#{plan_generator_agent.artifact_ref(:requirements)}`",
22
+ type: :markdown
23
+ }
24
+ }
25
+ end
26
+
27
+ # Execute the agent to generate some output artifacts based on some input artifacts.
28
+ #
29
+ # @param requirements [String] The initial requirements.
30
+ # @return [Hash{Symbol => Object}] Output artifacts content
31
+ def run(requirements:)
32
+ user_instructions = {
33
+ ordered_list: [
34
+ "Read the initial requirements from the artifact named `#{plan_generator_agent.artifact_ref(:requirements)}`",
35
+ 'Analyze the project files',
36
+ "Create an artifact named `#{plan_generator_agent.artifact_ref(:plan)}` with a complete and detailed " \
37
+ 'step-by-step implementation plan in Markdown format'
38
+ ]
39
+ }
40
+ loop do
41
+ step_agent(plan_generator_agent, user_instructions:)
42
+ @artifacts[:plan].strip!
43
+ content, user_prompt = Helpers.review_content(
44
+ reviews_dir: "#{@session_dir}/reviews",
45
+ name: 'plan.md',
46
+ description: 'Implementation plan',
47
+ editable: true,
48
+ promptable: true,
49
+ content: @artifacts[:plan]
50
+ )
51
+ diffs =
52
+ if @artifacts[:plan] == content
53
+ nil
54
+ else
55
+ Diffy::Diff.new("#{@artifacts[:plan].strip}\n", "#{content.strip}\n", context: 3, include_diff_info: true).to_s
56
+ end
57
+ @artifacts[:plan] = content
58
+ break if user_prompt.empty?
59
+
60
+ user_instructions = <<~EO_INSTRUCTIONS
61
+ #{user_prompt}
62
+
63
+ Re-create the artifact named `#{plan_generator_agent.artifact_ref(:plan)}` with a revised implementation plan, taking the above user guidance into account.
64
+ EO_INSTRUCTIONS
65
+ user_instructions << <<~EO_INSTRUCTIONS if diffs
66
+
67
+ The user performed the following modifications on your implementation plan.
68
+ You have to take them into account while revising the plan.
69
+
70
+ ```
71
+ #{
72
+ # Remove the 2 first lines (headers of temporary file names).
73
+ diffs.to_s.split("\n")[2..].join("\n").strip
74
+ }
75
+ ```
76
+ EO_INSTRUCTIONS
77
+ end
78
+ { plan: @artifacts[:plan] }
79
+ end
80
+
81
+ # Get a Plan Generator agent.
82
+ #
83
+ # @return [Agent] The Plan Generator agent
84
+ def plan_generator_agent
85
+ @plan_generator_agent ||= new_agent(PlanGeneratorAgent, **Config.agent_options['free_complex_planning'])
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,144 @@
1
+ module XAeonAgents
2
+ module Agents
3
+ # Agent responsible for creating a Pull Request of the current branch against its base reference on Github.
4
+ class PullRequestCreatorAgent < ComposableAgents::Agent
5
+ prepend AgentDefaults
6
+
7
+ # Define input artifacts contracts
8
+ #
9
+ # @return [Hash{Symbol => Object}] Set of input artifacts description, per artifact name
10
+ def input_artifacts_contracts
11
+ {
12
+ base_sha: 'The git ref of the base of the feature branch',
13
+ requirements: {
14
+ description: 'The initial requirements',
15
+ optional: true
16
+ }
17
+ }
18
+ end
19
+
20
+ # Constructor
21
+ #
22
+ # @param authors [Array<Agent>] List of agents that should be credited as authors of this commit
23
+ # @param agent_params [Hash{Symbol => Object}] Extra agent parameters
24
+ def initialize(authors: [], **agent_params)
25
+ super(name: 'Pull Request Creator', **agent_params)
26
+ @authors = authors
27
+ end
28
+
29
+ # Execute the agent to generate some output artifacts based on some input artifacts.
30
+ #
31
+ # @param base_sha [String] The git reference of the base of the branch.
32
+ # @param requirements [String, nil] The initial requirements, or nil if none.
33
+ # @return [Hash{Symbol => Object}] Output artifacts content
34
+ def run(base_sha:, requirements: nil)
35
+ raise 'Unable to find the Github repository' unless Helpers.github_repo
36
+
37
+ repo_name = Helpers.github_repo
38
+ head_branch = Helpers.git.current_branch
39
+
40
+ # Push the branch on the git_remote using --force-with-lease as it may have been rebased
41
+ # TODO: Use force_with_lease when it will be supported by ruby-git
42
+ Helpers.git.push(Helpers.github_remote, head_branch, force: true)
43
+
44
+ # Check if PR already exists for the current branch
45
+ existing_pr = Helpers.github.pull_requests(repo_name, state: 'open').find { |pull_request| pull_request.head.ref == head_branch }
46
+ if existing_pr.nil?
47
+ # Create new PR
48
+ git_diff_interpreter_agent = new_agent(GitDiffInterpreterAgent)
49
+ step_agent(git_diff_interpreter_agent, git_ref_base: base_sha)
50
+ step(:create_pr) do
51
+ sections = [@artifacts[:change_intent].strip]
52
+ sections << <<~EO_SECTION if requirements
53
+ # Initial requirements given
54
+
55
+ #{ComposableAgents::Utils::Markdown.align_markdown_headers(requirements, level: 2)}
56
+ EO_SECTION
57
+ full_messages = @authors
58
+ .map do |author|
59
+ if author.respond_to?(:conversation)
60
+ # Only keep single user prompts and agent's questions with their corresponding user's answer
61
+ messages = []
62
+ # Skip the first user instruction
63
+ remaining_conversation =
64
+ if !author.conversation.empty? && author.conversation.first[:author].downcase == 'user'
65
+ author.conversation[1..]
66
+ else
67
+ author.conversation
68
+ end
69
+ until remaining_conversation.empty?
70
+ message = remaining_conversation.shift
71
+ next if message[:message].strip.empty?
72
+
73
+ if message[:author].downcase == 'user'
74
+ messages << message
75
+ elsif message[:question]
76
+ answer = remaining_conversation.first
77
+ messages <<
78
+ if answer && answer[:author].downcase == 'user' && !answer[:message].strip.empty?
79
+ message.merge(answer: remaining_conversation.shift)
80
+ else
81
+ message
82
+ end
83
+ end
84
+ end
85
+ messages
86
+ else
87
+ []
88
+ end
89
+ end
90
+ .flatten(1)
91
+ .sort_by { |message| message[:at] }
92
+ sections << <<~EO_SECTION unless full_messages.empty?
93
+ # User guidance and feedback to agents
94
+
95
+ #{
96
+ full_messages
97
+ .map do |message|
98
+ <<~EO_MESSAGE.strip
99
+ › **#{message[:author]}**
100
+ #{message[:message].each_line.map { |line| "> #{line}".strip }.join("\n")}
101
+ > <sub>#{message[:at]}</sub>
102
+ #{
103
+ if message[:answer]
104
+ <<~EO_ANSWER
105
+ >
106
+ > › **#{message[:answer][:author]}**
107
+ #{message[:answer][:message].each_line.map { |line| "> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#{line}" }.join("\n")}
108
+ > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<sub>#{message[:answer][:at]}</sub>
109
+ EO_ANSWER
110
+ end
111
+ }
112
+ EO_MESSAGE
113
+ end
114
+ .join("\n\n")
115
+ }
116
+ EO_SECTION
117
+ sections << <<~EO_SECTION unless @authors.empty?
118
+ # Co-authored by X-Aeon AI Agents
119
+
120
+ #{
121
+ (@authors + [git_diff_interpreter_agent.diff_interpreter_agent]).map do |agent|
122
+ "- #{agent.full_name}"
123
+ end.join("\n")
124
+ }
125
+ EO_SECTION
126
+ new_pr = Helpers.github.create_pull_request(
127
+ repo_name,
128
+ base_sha,
129
+ head_branch,
130
+ @artifacts[:one_line_summary].strip,
131
+ sections.map(&:strip).join("\n\n")
132
+ )
133
+ # TODO: Make that a log_info
134
+ log_debug "Created new Pull Request for branch #{head_branch}: #{new_pr.html_url}"
135
+ end
136
+ else
137
+ # TODO: Make that a log_info
138
+ log_debug "A Pull Request for branch #{head_branch} already exists: #{existing_pr.html_url}"
139
+ end
140
+ {}
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,55 @@
1
+ module XAeonAgents
2
+ module Agents
3
+ # Collection of agents used to generate README parts
4
+ module Readme
5
+ # Agent responsible for generating the "About" section of a README.
6
+ class AboutAnalyzerAgent < ComposableAgents::Cline::Agent
7
+ prepend AgentDefaults
8
+
9
+ # Define output artifacts contracts
10
+ #
11
+ # @return [Hash{Symbol => Object}] Set of output artifacts description, per artifact name
12
+ def output_artifacts_contracts
13
+ super.merge(
14
+ about: {
15
+ description: 'The "About" section content in Markdown format, describing the project goal, problem it solves, and its interface',
16
+ type: :markdown
17
+ },
18
+ name: {
19
+ description: 'This project\'s name',
20
+ type: :text
21
+ },
22
+ description: {
23
+ description: 'This project\'s description in 1 line',
24
+ type: :markdown
25
+ }
26
+ )
27
+ end
28
+
29
+ # Constructor
30
+ #
31
+ # @param agent_params [Hash{Symbol => Object}] Extra agent parameters
32
+ def initialize(**agent_params)
33
+ super(
34
+ name: 'About Analyzer',
35
+ objective: <<~EO_OBJECTIVE,
36
+ Analyze the project's code, features and layout to understand its purpose and interface.
37
+ Provide the following information:
38
+ - The project's name (for example Rubygem's name, released package name, repository's name...).
39
+ - The 1-line description of the project, using Markdown.
40
+ - A high-level overview (about section) of the project in Markdown format, compatible with Github flavor.
41
+ EO_OBJECTIVE
42
+ constraints: <<~EO_CONSTRAINTS,
43
+ - The required output artifacts content should only contain the required content, without additional header or title.
44
+ - Do not explain or describe the artifact contents: only provide the content as required.
45
+ - Only focus on the specific scope of the required documentation required.
46
+ Other agents will generate other sections of the README.
47
+ Do not try to document other parts.
48
+ EO_CONSTRAINTS
49
+ **agent_params
50
+ )
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end