-
Notifications
You must be signed in to change notification settings - Fork 23
DMC-791 [CORE] Agent Projects Setup Overview #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.github.istin.dmtools.di; | ||
|
|
||
| import com.github.istin.dmtools.projectsetup.agent.FinalStatusDetectionAgent; | ||
| import dagger.Component; | ||
|
|
||
| import javax.inject.Singleton; | ||
|
|
||
| @Singleton | ||
| @Component(modules = {ConfigurationModule.class, AIComponentsModule.class}) | ||
| public interface FinalStatusDetectionAgentComponent { | ||
| void inject(FinalStatusDetectionAgent finalStatusDetectionAgent); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.github.istin.dmtools.di; | ||
|
|
||
| import com.github.istin.dmtools.projectsetup.agent.ProjectSetupAnalysisAgent; | ||
| import dagger.Component; | ||
|
|
||
| import javax.inject.Singleton; | ||
|
|
||
| @Singleton | ||
| @Component(modules = {ConfigurationModule.class, AIComponentsModule.class}) | ||
| public interface ProjectSetupAnalysisAgentComponent { | ||
| void inject(ProjectSetupAnalysisAgent projectSetupAnalysisAgent); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.github.istin.dmtools.di; | ||
|
|
||
| import com.github.istin.dmtools.projectsetup.agent.StoryDescriptionWritingRulesAgent; | ||
| import dagger.Component; | ||
|
|
||
| import javax.inject.Singleton; | ||
|
|
||
| @Singleton | ||
| @Component(modules = {ConfigurationModule.class, AIComponentsModule.class}) | ||
| public interface StoryDescriptionWritingRulesAgentComponent { | ||
| void inject(StoryDescriptionWritingRulesAgent storyDescriptionWritingRulesAgent); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.github.istin.dmtools.di; | ||
|
|
||
| import com.github.istin.dmtools.projectsetup.agent.TestCaseWritingRulesAgent; | ||
| import dagger.Component; | ||
|
|
||
| import javax.inject.Singleton; | ||
|
|
||
| @Singleton | ||
| @Component(modules = {ConfigurationModule.class, AIComponentsModule.class}) | ||
| public interface TestCaseWritingRulesAgentComponent { | ||
| void inject(TestCaseWritingRulesAgent testCaseWritingRulesAgent); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.github.istin.dmtools.di; | ||
|
|
||
| import com.github.istin.dmtools.projectsetup.agent.WorkflowAnalysisAgent; | ||
| import dagger.Component; | ||
|
|
||
| import javax.inject.Singleton; | ||
|
|
||
| @Singleton | ||
| @Component(modules = {ConfigurationModule.class, AIComponentsModule.class}) | ||
| public interface WorkflowAnalysisAgentComponent { | ||
| void inject(WorkflowAnalysisAgent workflowAnalysisAgent); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,266 @@ | ||||||||||||||||||||||||
| package com.github.istin.dmtools.projectsetup; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import com.github.istin.dmtools.atlassian.jira.JiraClient; | ||||||||||||||||||||||||
| import com.github.istin.dmtools.common.model.ITicket; | ||||||||||||||||||||||||
| import com.github.istin.dmtools.common.model.ToText; | ||||||||||||||||||||||||
| import com.github.istin.dmtools.common.tracker.TrackerClient; | ||||||||||||||||||||||||
| import com.github.istin.dmtools.di.ServerManagedIntegrationsModule; | ||||||||||||||||||||||||
| import com.github.istin.dmtools.job.AbstractJob; | ||||||||||||||||||||||||
| import com.github.istin.dmtools.projectsetup.agent.*; | ||||||||||||||||||||||||
| import lombok.Getter; | ||||||||||||||||||||||||
| import org.json.JSONArray; | ||||||||||||||||||||||||
| import org.json.JSONObject; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import javax.inject.Inject; | ||||||||||||||||||||||||
| import javax.inject.Singleton; | ||||||||||||||||||||||||
| import java.util.ArrayList; | ||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||
| import java.util.stream.Collectors; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import dagger.Component; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public class ProjectSetupAnalysisJob extends AbstractJob<ProjectSetupAnalysisJobParams, JSONObject> { | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Inject | ||||||||||||||||||||||||
| TrackerClient<? extends ITicket> trackerClient; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Inject | ||||||||||||||||||||||||
| FinalStatusDetectionAgent finalStatusDetectionAgent; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Inject | ||||||||||||||||||||||||
| ProjectSetupAnalysisAgent projectSetupAnalysisAgent; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Inject | ||||||||||||||||||||||||
| WorkflowAnalysisAgent workflowAnalysisAgent; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Inject | ||||||||||||||||||||||||
| StoryDescriptionWritingRulesAgent storyDescriptionWritingRulesAgent; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Inject | ||||||||||||||||||||||||
| TestCaseWritingRulesAgent testCaseWritingRulesAgent; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Getter | ||||||||||||||||||||||||
| @Inject | ||||||||||||||||||||||||
| com.github.istin.dmtools.ai.AI ai; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Singleton | ||||||||||||||||||||||||
| @Component(modules = {com.github.istin.dmtools.di.ConfigurationModule.class, | ||||||||||||||||||||||||
| com.github.istin.dmtools.di.JiraModule.class, | ||||||||||||||||||||||||
| com.github.istin.dmtools.di.AIComponentsModule.class, | ||||||||||||||||||||||||
| com.github.istin.dmtools.di.ConfluenceModule.class, | ||||||||||||||||||||||||
| com.github.istin.dmtools.di.AIAgentsModule.class}) | ||||||||||||||||||||||||
| public interface ProjectSetupAnalysisJobComponent { | ||||||||||||||||||||||||
| void inject(ProjectSetupAnalysisJob projectSetupAnalysisJob); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Singleton | ||||||||||||||||||||||||
| @Component(modules = {ServerManagedIntegrationsModule.class, com.github.istin.dmtools.di.AIAgentsModule.class}) | ||||||||||||||||||||||||
| public interface ServerManagedProjectSetupAnalysisJobComponent { | ||||||||||||||||||||||||
| void inject(ProjectSetupAnalysisJob projectSetupAnalysisJob); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public ProjectSetupAnalysisJob() { | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||
| protected void initializeStandalone() { | ||||||||||||||||||||||||
| DaggerProjectSetupAnalysisJob_ProjectSetupAnalysisJobComponent.create().inject(this); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||
| protected void initializeServerManaged(org.json.JSONObject resolvedIntegrations) { | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| ServerManagedIntegrationsModule module = new ServerManagedIntegrationsModule(resolvedIntegrations); | ||||||||||||||||||||||||
| ServerManagedProjectSetupAnalysisJobComponent component = | ||||||||||||||||||||||||
| DaggerProjectSetupAnalysisJob_ServerManagedProjectSetupAnalysisJobComponent.builder() | ||||||||||||||||||||||||
| .serverManagedIntegrationsModule(module) | ||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||
| component.inject(this); | ||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||
| throw new RuntimeException("Failed to initialize ProjectSetupAnalysisJob in server-managed mode", e); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
| /** | |
| * Executes the project setup analysis job for the given project. | |
| * | |
| * @param params The job parameters containing the project key. | |
| * @return A {@link JSONObject} containing aggregated analysis results from all agents. | |
| * @throws IllegalArgumentException if the project key is null or empty. | |
| * @throws Exception if any agent execution or data retrieval fails. | |
| */ |
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cast from TrackerClient to JiraClient is unsafe without a type check. If the trackerClient implementation is not actually a JiraClient, this will throw a ClassCastException at runtime. Add a type check before casting:
if (!(trackerClient instanceof JiraClient)) {
throw new IllegalStateException("TrackerClient must be an instance of JiraClient for this job");
}
JiraClient<? extends ITicket> jiraClient = (JiraClient<? extends ITicket>) trackerClient;| // Cast TrackerClient to JiraClient to access getIssueTypes and getFields methods | |
| // Cast TrackerClient to JiraClient to access getIssueTypes and getFields methods | |
| if (!(trackerClient instanceof JiraClient)) { | |
| throw new IllegalStateException("TrackerClient must be an instance of JiraClient for this job"); | |
| } |
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing import statement for JiraClient. The code uses JiraClient at line 101 but doesn't import it. Add the import:
import com.github.istin.dmtools.atlassian.jira.JiraClient;There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ClassCastException when using non-Jira trackers
High Severity
The job unconditionally casts trackerClient to JiraClient to call getIssueTypes() and getFields(). In server-managed mode, ServerManagedIntegrationsModule.provideTrackerClient() can return AzureDevOpsClient or RallyClient instances, which do not extend JiraClient. This cast throws a ClassCastException at runtime when the configured tracker is Azure DevOps or Rally, making the job incompatible with non-Jira trackers.
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constructor new JSONArray(issueTypes) will not correctly serialize a List of IssueType objects. The JSONArray constructor that takes a Collection will call toString() on each element, which may not produce valid JSON structure. Convert the list properly:
JSONArray issueTypesArray = new JSONArray();
for (IssueType issueType : issueTypes) {
issueTypesArray.put(issueType.getJSONObject());
}
String issueTypesJson = issueTypesArray.toString();Then pass issueTypesJson to the Params constructor.
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing JavaDoc documentation. Add documentation:
/**
* Retrieves completed tickets for the specified project based on final statuses.
* Returns the last 50 tickets ordered by updated date descending.
*
* @param projectKey The Jira project key
* @param finalStatuses The array of final status names to query, or null to use defaults
* @return A list of completed tickets, limited to the most recent 50
* @throws Exception if ticket retrieval fails
*/
private List<? extends ITicket> getCompletedTickets(String projectKey, JSONArray finalStatuses) throws Exception {| /** | |
| * Retrieves completed tickets for the specified project based on final statuses. | |
| * Returns the last 50 tickets ordered by updated date descending. | |
| * | |
| * @param projectKey The Jira project key | |
| * @param finalStatuses The array of final status names to query, or null to use defaults | |
| * @return A list of completed tickets, limited to the most recent 50 | |
| * @throws Exception if ticket retrieval fails | |
| */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Verified via automated test of github_add_inline_comment MCP tool (auto-fetched commitId).
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The magic number 50 for limiting tickets should be defined as a named constant for better maintainability:
private static final int MAX_COMPLETED_TICKETS = 50;Then use:
return allTickets.stream()
.limit(MAX_COMPLETED_TICKETS)
.collect(Collectors.toList());
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing JavaDoc documentation for private helper methods. Add documentation for better code maintainability:
/**
* Formats a list of tickets into a text representation suitable for AI analysis.
* Falls back to basic ticket information if full ticket text conversion fails.
*
* @param tickets The list of tickets to format
* @return A formatted string with ticket data separated by delimiters
*/
private String formatTicketsForAnalysis(List<? extends ITicket> tickets) {| /** | |
| * Formats a list of tickets into a text representation suitable for AI analysis. | |
| * Attempts to use the ticket's {@code toText()} method; if that fails, falls back to | |
| * basic ticket information (key, title, description). If all else fails, uses only the ticket key. | |
| * | |
| * @param tickets the list of tickets to format | |
| * @return a formatted string with ticket data separated by delimiters | |
| */ |
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing JavaDoc documentation. Add documentation:
/**
* Extracts story descriptions from the provided tickets across all issue types.
*
* @param tickets The list of tickets to extract descriptions from
* @return A formatted string containing ticket descriptions separated by delimiters
*/
private String extractStoryDescriptions(List<? extends ITicket> tickets) {| /** | |
| * Extracts story descriptions from the provided tickets across all issue types. | |
| * | |
| * @param tickets The list of tickets to extract descriptions from | |
| * @return A formatted string containing ticket descriptions separated by delimiters | |
| */ |
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing JavaDoc documentation. Add documentation:
/**
* Extracts test case data from the provided tickets across all issue types.
*
* @param tickets The list of tickets to extract test case data from
* @return A formatted string containing ticket summaries and descriptions separated by delimiters
*/
private String extractTestCaseData(List<? extends ITicket> tickets) {| /** | |
| * Extracts test case data from the provided tickets across all issue types. | |
| * | |
| * @param tickets The list of tickets to extract test case data from | |
| * @return A formatted string containing ticket summaries and descriptions separated by delimiters | |
| */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.github.istin.dmtools.projectsetup; | ||
|
|
||
| import com.github.istin.dmtools.job.Params; | ||
| import com.google.gson.annotations.SerializedName; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Data; | ||
| import lombok.EqualsAndHashCode; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Data | ||
| @EqualsAndHashCode(callSuper = false) | ||
|
||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class ProjectSetupAnalysisJobParams extends Params { | ||
|
|
||
| public static final String PROJECT_KEY = "projectKey"; | ||
|
|
||
| @SerializedName(PROJECT_KEY) | ||
| private String projectKey; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method overrides AbstractJob<ProjectSetupAnalysisJobParams,JSONObject>.getAi; it is advisable to add an Override annotation.