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.
- checksums.yaml +7 -0
- data/AGENTS.md +136 -0
- data/CHANGELOG.md +398 -0
- data/README.md +579 -0
- data/TODO.md +48 -0
- data/bin/xaa +5 -0
- data/lib/x_aeon_agents/agent_defaults.rb +60 -0
- data/lib/x_aeon_agents/agent_options.rb +58 -0
- data/lib/x_aeon_agents/agents/coder_agent.rb +82 -0
- data/lib/x_aeon_agents/agents/committer_agent.rb +71 -0
- data/lib/x_aeon_agents/agents/developer_agent.rb +235 -0
- data/lib/x_aeon_agents/agents/diff_interpreter_agent.rb +80 -0
- data/lib/x_aeon_agents/agents/documenter_agent.rb +44 -0
- data/lib/x_aeon_agents/agents/executor_agent.rb +9 -0
- data/lib/x_aeon_agents/agents/feedback_analyst_agent.rb +62 -0
- data/lib/x_aeon_agents/agents/gh_comments.gql +64 -0
- data/lib/x_aeon_agents/agents/git_diff_interpreter_agent.rb +57 -0
- data/lib/x_aeon_agents/agents/issue_implementer_agent.rb +88 -0
- data/lib/x_aeon_agents/agents/one_line_code_diff_summarizer_agent.rb +57 -0
- data/lib/x_aeon_agents/agents/plan_generator_agent.rb +51 -0
- data/lib/x_aeon_agents/agents/planner_agent.rb +89 -0
- data/lib/x_aeon_agents/agents/pull_request_creator_agent.rb +144 -0
- data/lib/x_aeon_agents/agents/readme/about_analyzer_agent.rb +55 -0
- data/lib/x_aeon_agents/agents/readme/contributing_agent.rb +47 -0
- data/lib/x_aeon_agents/agents/readme/development_agent.rb +54 -0
- data/lib/x_aeon_agents/agents/readme/documentation_agent.rb +43 -0
- data/lib/x_aeon_agents/agents/readme/features_agent.rb +45 -0
- data/lib/x_aeon_agents/agents/readme/how_it_works_agent.rb +47 -0
- data/lib/x_aeon_agents/agents/readme/license_agent.rb +43 -0
- data/lib/x_aeon_agents/agents/readme/public_api_agent.rb +44 -0
- data/lib/x_aeon_agents/agents/readme/quick_start_agent.rb +50 -0
- data/lib/x_aeon_agents/agents/readme/requirements_agent.rb +43 -0
- data/lib/x_aeon_agents/agents/readme_generator_agent.rb +438 -0
- data/lib/x_aeon_agents/agents/review_resolver_agent.rb +236 -0
- data/lib/x_aeon_agents/agents/review_responder_agent.rb +64 -0
- data/lib/x_aeon_agents/agents/skill_generator_agent.rb +95 -0
- data/lib/x_aeon_agents/agents/skill_installer_agent.rb +103 -0
- data/lib/x_aeon_agents/agents/task_starter_agent.rb +66 -0
- data/lib/x_aeon_agents/agents/tester_agent.rb +44 -0
- data/lib/x_aeon_agents/cli.rb +384 -0
- data/lib/x_aeon_agents/config.rb +110 -0
- data/lib/x_aeon_agents/gen_helpers.rb +270 -0
- data/lib/x_aeon_agents/helpers.rb +211 -0
- data/lib/x_aeon_agents/logger.rb +21 -0
- data/lib/x_aeon_agents/providers/cline.rb +78 -0
- data/lib/x_aeon_agents/version.rb +6 -0
- data/lib/x_aeon_agents.rb +38 -0
- 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| "> #{line}" }.join("\n")}
|
|
108
|
+
> <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
|