From 8d6b71d1c4beb8bc2450fa66e7ee9b6758a967f8 Mon Sep 17 00:00:00 2001 From: Robin Olsen Date: Wed, 25 Feb 2026 15:14:09 +0100 Subject: [PATCH] add multiple new tasks, validation not implemented --- cmd/survey/main.go | 29 ++++- cmd/survey/tasks/backlogRefinement.go | 128 +++++++++++++++++++++++ cmd/survey/tasks/codingTask.go | 16 ++- cmd/survey/tasks/createIssue.go | 24 +++-- cmd/survey/tasks/dependencyManagement.go | 120 +++++++++++++++++++++ cmd/survey/tasks/gitTask.go | 22 ++-- cmd/survey/tasks/issueTriage.go | 120 +++++++++++++++++++++ cmd/survey/tasks/milestoneTracking.go | 120 +++++++++++++++++++++ cmd/survey/tasks/priorityManagement.go | 120 +++++++++++++++++++++ cmd/survey/tasks/reportGeneration.go | 122 +++++++++++++++++++++ cmd/survey/tasks/sprintPlanning.go | 121 +++++++++++++++++++++ cmd/survey/tasks/stakeholderUpdate.go | 115 ++++++++++++++++++++ cmd/survey/tasks/teamCapacity.go | 127 ++++++++++++++++++++++ internal/models/issue.go | 1 - 14 files changed, 1165 insertions(+), 20 deletions(-) create mode 100644 cmd/survey/tasks/backlogRefinement.go create mode 100644 cmd/survey/tasks/dependencyManagement.go create mode 100644 cmd/survey/tasks/issueTriage.go create mode 100644 cmd/survey/tasks/milestoneTracking.go create mode 100644 cmd/survey/tasks/priorityManagement.go create mode 100644 cmd/survey/tasks/reportGeneration.go create mode 100644 cmd/survey/tasks/sprintPlanning.go create mode 100644 cmd/survey/tasks/stakeholderUpdate.go create mode 100644 cmd/survey/tasks/teamCapacity.go diff --git a/cmd/survey/main.go b/cmd/survey/main.go index 7191805..8c1e4bb 100644 --- a/cmd/survey/main.go +++ b/cmd/survey/main.go @@ -40,8 +40,35 @@ func init() { task.RegisterTask("coding_task", func(app *service.App) task.Tasker { return tasks.NewCodingTask(app) }) - task.RegisterTask("git-task", func(app *service.App) task.Tasker { + task.RegisterTask("git_task", func(app *service.App) task.Tasker { return tasks.NewGitTask(app) }) + task.RegisterTask("sprint_planning", func(app *service.App) task.Tasker { + return tasks.NewSprintPlanningTask(app) + }) + task.RegisterTask("issue_triage", func(app *service.App) task.Tasker { + return tasks.NewIssueTriageTask(app) + }) + task.RegisterTask("milestone_tracking", func(app *service.App) task.Tasker { + return tasks.NewMilestoneTrackingTask(app) + }) + task.RegisterTask("dependency_management", func(app *service.App) task.Tasker { + return tasks.NewDependencyManagementTask(app) + }) + task.RegisterTask("team_capacity", func(app *service.App) task.Tasker { + return tasks.NewTeamCapacityTask(app) + }) + task.RegisterTask("report_generation", func(app *service.App) task.Tasker { + return tasks.NewReportGenerationTask(app) + }) + task.RegisterTask("stakeholder_update", func(app *service.App) task.Tasker { + return tasks.NewStakeholderUpdateTask(app) + }) + task.RegisterTask("priority_management", func(app *service.App) task.Tasker { + return tasks.NewPriorityManagementTask(app) + }) + task.RegisterTask("backlog_refinement", func(app *service.App) task.Tasker { + return tasks.NewBacklogRefinementTask(app) + }) } diff --git a/cmd/survey/tasks/backlogRefinement.go b/cmd/survey/tasks/backlogRefinement.go new file mode 100644 index 0000000..0fd6173 --- /dev/null +++ b/cmd/survey/tasks/backlogRefinement.go @@ -0,0 +1,128 @@ +package tasks + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/pkg/task" + taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui" + "github.com/charmbracelet/huh" +) + +const backlogRefinementDescription = `You are tasked with backlog refinement. + +The product backlog has become cluttered with old and unclear issues. You need to groom the backlog: + +1. Review all issues in the backlog +2. Identify stale or obsolete issues (older items that are no longer relevant) +3. Update issue descriptions for clarity where needed +4. Close issues that are duplicates or no longer applicable +5. Reprioritize issues based on current business value +6. Ensure remaining issues are well-defined and actionable + +Focus on making the backlog a reliable source of upcoming work.` + +type BacklogRefinementTask struct { + done bool + app *service.App + setupIssue *models.Issue +} + +func NewBacklogRefinementTask(app *service.App) *BacklogRefinementTask { + return &BacklogRefinementTask{app: app, done: false} +} + +func (t *BacklogRefinementTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/refinement-task-stats.json") +} + +func (t *BacklogRefinementTask) Details() taskui.TaskDetails { + return BaseDetails(). + WithTitle("Backlog Refinement Task"). + WithDescription(backlogRefinementDescription). + WithTimeToComplete("12m"). + WithDifficulty("Medium") +} + +func (t *BacklogRefinementTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[int](). + Title("How many issues did you close or update during refinement?"). + Options( + huh.NewOption("1-2", 1), + huh.NewOption("3-4", 2), + huh.NewOption("5+", 3), + ), + ), + ) +} + +func (t *BacklogRefinementTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + refinementIssues := []*models.Issue{ + models.NewIssueBuilder(). + WithTitle("Old feature request: Fax integration"). + WithDescription("Allow sending reports via fax. DEPRECATED - nobody uses fax anymore"). + WithPriority(3). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("User profile page"). + WithDescription("Create page for users to view profile. DUPLICATE of user-management epic"). + WithPriority(2). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Mobile app redesign"). + WithDescription("Redesign mobile interface with modern UI patterns. Still relevant, needs clarity"). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Legacy data export tool"). + WithDescription("Tool for exporting data in old format. OBSOLETE - format no longer supported"). + WithPriority(3). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("API v1 documentation"). + WithDescription("Document old API version. DEPRECATED - migrating to v2"). + WithPriority(3). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Customer feedback system"). + WithDescription("Build system for collecting user feedback. HIGH VALUE - prioritize"). + WithPriority(2). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + } + + if err := t.app.Issues.CreateIssues(ctx, refinementIssues, ""); err != nil { + return err + } + + t.setupIssue = models.NewBaseIssue(). + WithTitle("Backlog Refinement Session"). + WithDescription(backlogRefinementDescription). + Build() + + return t.app.Issues.CreateIssue(ctx, t.setupIssue, "") +} + +func (t *BacklogRefinementTask) Validate(ctx context.Context) (bool, error) { + + return EndTaskWithTimeout(&t.done, "Backlog refinement task completed!", 5*time.Second) +} diff --git a/cmd/survey/tasks/codingTask.go b/cmd/survey/tasks/codingTask.go index 3250549..743c85b 100644 --- a/cmd/survey/tasks/codingTask.go +++ b/cmd/survey/tasks/codingTask.go @@ -15,8 +15,20 @@ import ( const codingDescription = `You are tasked with writing a simple function. -Write a function that takes two integers and returns their sum. -The function should be named "Add" and be part of the "coding" package.` +This task will test your ability to write clean, working code. + +Your task: +1. Review the requirements below +2. Write a function that takes two integers and returns their sum +3. The function should be named "Add" +4. The function should be part of the "coding" package +5. Save your code to the code.txt file + +Requirements: +- Function name: Add +- Parameters: two integers +- Return value: integer (sum of the two inputs) +- Package: coding` var textFileContent = codingDescription + ` Please write your code below this line! diff --git a/cmd/survey/tasks/createIssue.go b/cmd/survey/tasks/createIssue.go index aad6661..50daa3b 100644 --- a/cmd/survey/tasks/createIssue.go +++ b/cmd/survey/tasks/createIssue.go @@ -12,11 +12,17 @@ import ( ) const description = `You are tasked with creating a new issue in the project management system. + This task will test your ability to use the issue creation workflow effectively. -Assign this task to yourself and start creating a new issue. -Make sure to fill out all the necessary details, including the title and description. -Once you have created the issue, mark it as closed to complete the task.` +Your task: +1. Create a new issue with a clear title +2. Add a detailed description explaining what needs to be done +3. Assign the issue to yourself +4. Mark the issue as in-progress when you start working on it +5. Close the issue once you've completed the work + +Make sure to fill out all the necessary details to help others understand the work item.` type CreateIssueTask struct { done bool @@ -46,7 +52,9 @@ func (t *CreateIssueTask) Setup(ctx context.Context) error { } t.setupTask = models.NewBaseIssue(). - WithTitle("Create a New Issue").WithDescription(description).Build() + WithTitle("Create a New Issue"). + WithDescription(description). + Build() return t.app.Issues.CreateIssue(ctx, t.setupTask, "") } @@ -57,11 +65,9 @@ func (t *CreateIssueTask) Validate(ctx context.Context) (bool, error) { return false, err } - /* Not implemented yet, as assigning to self is not supported in the current implementation - if t.setupTask.Assignee != "Me" { + if t.setupTask.Assignee == "" { return false, fmt.Errorf("issue not assigned to self") } - */ if len(issues) < 2 { return false, fmt.Errorf("issue not created") @@ -87,11 +93,9 @@ func (t *CreateIssueTask) Validate(ctx context.Context) (bool, error) { return false, fmt.Errorf("issue description is empty") } - /* Not implemented yet, as assigning to self is not supported in the current implementation - if createdIssue.Assignee != "Me" { + if createdIssue.Assignee == "" { return false, fmt.Errorf("issue not assigned to self") } - */ if createdIssue.Status != models.StatusClosed { return false, fmt.Errorf("issue status is not closed") diff --git a/cmd/survey/tasks/dependencyManagement.go b/cmd/survey/tasks/dependencyManagement.go new file mode 100644 index 0000000..375e1b5 --- /dev/null +++ b/cmd/survey/tasks/dependencyManagement.go @@ -0,0 +1,120 @@ +package tasks + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/pkg/task" + taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui" + "github.com/charmbracelet/huh" +) + +const dependencyManagementDescription = `You are tasked with managing issue dependencies. + +Several issues in your project have dependencies on other issues. You need to: + +1. Review the dependency chain described in issue descriptions +2. Identify issues that are blocked by others +3. Prioritize work on foundational issues (those that unblock others) +4. Update issue statuses to reflect dependency resolution +5. Ensure no circular dependencies exist + +Resolving dependencies in the right order is critical for efficient team workflow.` + +type DependencyManagementTask struct { + done bool + app *service.App + setupIssue *models.Issue +} + +func NewDependencyManagementTask(app *service.App) *DependencyManagementTask { + return &DependencyManagementTask{app: app, done: false} +} + +func (t *DependencyManagementTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/dependency-task-stats.json") +} + +func (t *DependencyManagementTask) Details() taskui.TaskDetails { + return BaseDetails(). + WithTitle("Dependency Management Task"). + WithDescription(dependencyManagementDescription). + WithTimeToComplete("15m"). + WithDifficulty("Hard") +} + +func (t *DependencyManagementTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[int](). + Title("How many foundational issues did you identify?"). + Options( + huh.NewOption("1", 1), + huh.NewOption("2", 2), + huh.NewOption("3+", 3), + ), + ), + ) +} + +func (t *DependencyManagementTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + depIssues := []*models.Issue{ + models.NewIssueBuilder(). + WithTitle("Setup database connection"). + WithDescription("Configure database connection pool."). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Define API contract"). + WithDescription("Create OpenAPI spec."). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Implement user repository"). + WithDescription("Implement data access layer for user management."). + WithPriority(2). + WithStatus(models.StatusBlocked). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Create user endpoints"). + WithDescription("REST API for users."). + WithPriority(2). + WithStatus(models.StatusBlocked). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Build user profile UI"). + WithDescription("Frontend user profile page."). + WithPriority(3). + WithStatus(models.StatusBlocked). + WithIssueType(models.TypeTask). + Build(), + } + + if err := t.app.Issues.CreateIssues(ctx, depIssues, ""); err != nil { + return err + } + + t.setupIssue = models.NewBaseIssue(). + WithTitle("Dependency Management"). + WithDescription(dependencyManagementDescription). + Build() + + return t.app.Issues.CreateIssue(ctx, t.setupIssue, "") +} + +func (t *DependencyManagementTask) Validate(ctx context.Context) (bool, error) { + + return EndTaskWithTimeout(&t.done, "Dependency management task completed!", 5*time.Second) +} diff --git a/cmd/survey/tasks/gitTask.go b/cmd/survey/tasks/gitTask.go index f120866..fd78046 100644 --- a/cmd/survey/tasks/gitTask.go +++ b/cmd/survey/tasks/gitTask.go @@ -15,10 +15,18 @@ import ( "github.com/go-git/go-git/v6" ) -var ( - gitTaskDescription = `You are tasked with performing a Git operation. -This task will test your ability to use Git effectively.` -) +const gitTaskDescription = `You are tasked with performing a Git operation. + +This task will test your ability to use Git effectively within a project management workflow. + +Your task: +1. Initialize or open a Git repository +2. Review the current repository status +3. Make appropriate changes to complete the task +4. Commit your changes with a meaningful message +5. Update the task status to reflect completion + +The repository has been initialized in ./task/.git/ for you to work with.` type GitTask struct { setupIssue *models.Issue @@ -68,9 +76,11 @@ func (t *GitTask) Setup(ctx context.Context) error { []byte("This is a Git task. Please perform a Git operation here."), os.FileMode(os.O_WRONLY|os.O_CREATE)) - t.setupIssue = models.NewBaseIssue(). + t.setupIssue = models.NewIssueBuilder(). WithTitle("Git Task Setup Issue"). - WithDescription(gitTaskDescription).Build() + WithDescription(gitTaskDescription). + WithIssueType(models.TypeTask). + Build() if err := t.app.Issues.CreateIssue(ctx, t.setupIssue, ""); err != nil { return err diff --git a/cmd/survey/tasks/issueTriage.go b/cmd/survey/tasks/issueTriage.go new file mode 100644 index 0000000..a515f63 --- /dev/null +++ b/cmd/survey/tasks/issueTriage.go @@ -0,0 +1,120 @@ +package tasks + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/pkg/task" + taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui" + "github.com/charmbracelet/huh" +) + +const issueTriageDescription = `You are tasked with triaging incoming issues. + +The support team has submitted several bug reports and feature requests that need to be reviewed and prioritized. Your job is to: + +1. Review each incoming issue +2. Assign appropriate priority +3. Set the correct status +4. Identify if it's a bug, feature, or task +5. Leave comments explaining your decisions + +Make decisions quickly but thoughtfully. Not everything is high priority!` + +type IssueTriageTask struct { + done bool + app *service.App + setupIssue *models.Issue +} + +func NewIssueTriageTask(app *service.App) *IssueTriageTask { + return &IssueTriageTask{app: app, done: false} +} + +func (t *IssueTriageTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/triage-task-stats.json") +} + +func (t *IssueTriageTask) Details() taskui.TaskDetails { + return BaseDetails(). + WithTitle("Issue Triage Task"). + WithDescription(issueTriageDescription). + WithTimeToComplete("12m"). + WithDifficulty("Medium") +} + +func (t *IssueTriageTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[int](). + Title("How many Critical priority issues did you identify?"). + Options( + huh.NewOption("0", 0), + huh.NewOption("1", 1), + huh.NewOption("2", 2), + huh.NewOption("3+", 3), + ), + ), + ) +} + +func (t *IssueTriageTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + triageIssues := []*models.Issue{ + models.NewIssueBuilder(). + WithTitle("App crashes on login"). + WithDescription("Users report the app crashes immediately after entering credentials. Affects all users on Android 12. Needs urgent attention."). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Dark mode support"). + WithDescription("Users requesting dark mode theme for better night time usage. Nice to have feature."). + WithPriority(3). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Database timeout errors"). + WithDescription("Intermittent timeouts when querying large datasets. Needs investigation."). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Add export to CSV feature"). + WithDescription("Sales team needs ability to export reports to CSV format."). + WithPriority(2). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Security: Password reset vulnerability"). + WithDescription("Reported by security audit - password reset tokens don't expire. Critical security issue."). + WithPriority(0). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + } + + if err := t.app.Issues.CreateIssues(ctx, triageIssues, ""); err != nil { + return err + } + + t.setupIssue = models.NewBaseIssue(). + WithTitle("Issue Triage Queue"). + WithDescription(issueTriageDescription). + Build() + + return t.app.Issues.CreateIssue(ctx, t.setupIssue, "") +} + +func (t *IssueTriageTask) Validate(ctx context.Context) (bool, error) { + return EndTaskWithTimeout(&t.done, "Issue triage task completed!", 5*time.Second) +} diff --git a/cmd/survey/tasks/milestoneTracking.go b/cmd/survey/tasks/milestoneTracking.go new file mode 100644 index 0000000..ab69f85 --- /dev/null +++ b/cmd/survey/tasks/milestoneTracking.go @@ -0,0 +1,120 @@ +package tasks + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/pkg/task" + taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui" + "github.com/charmbracelet/huh" +) + +const milestoneTrackingDescription = `You are tasked with managing a project milestone. + +The "Q1 Release" milestone is approaching and you need to ensure all issues are on track. +Review the milestone issues and: + +1. Identify issues at risk of missing the deadline +2. Update issue statuses to reflect current progress +3. Flag any blockers or dependencies causing delays +4. Close completed issues +5. Provide status updates for stakeholder visibility + +The milestone deadline is 2 weeks away. Some issues have dependencies that need to be resolved first.` + +type MilestoneTrackingTask struct { + done bool + app *service.App + setupIssue *models.Issue +} + +func NewMilestoneTrackingTask(app *service.App) *MilestoneTrackingTask { + return &MilestoneTrackingTask{app: app, done: false} +} + +func (t *MilestoneTrackingTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/milestone-task-stats.json") +} + +func (t *MilestoneTrackingTask) Details() taskui.TaskDetails { + return BaseDetails(). + WithTitle("Milestone Tracking Task"). + WithDescription(milestoneTrackingDescription). + WithTimeToComplete("10m"). + WithDifficulty("Easy") +} + +func (t *MilestoneTrackingTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[int](). + Title("How many issues did you identify as at-risk?"). + Options( + huh.NewOption("0", 0), + huh.NewOption("1-2", 1), + huh.NewOption("3+", 2), + ), + ), + ) +} + +func (t *MilestoneTrackingTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + milestoneIssues := []*models.Issue{ + models.NewIssueBuilder(). + WithTitle("Core API endpoints"). + WithDescription("Implement REST API for core functionality. COMPLETED"). + WithPriority(1). + WithStatus(models.StatusClosed). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Frontend dashboard"). + WithDescription("Create main dashboard UI. In progress - 80%% complete"). + WithPriority(1). + WithStatus(models.StatusInProgress). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("User management module"). + WithDescription("Depends on Core API. Add user CRUD operations. Currently blocked"). + WithPriority(2). + WithStatus(models.StatusBlocked). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Analytics reporting"). + WithDescription("Generate usage analytics reports. Not started yet"). + WithPriority(3). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Email notifications"). + WithDescription("Setup email service for alerts. 50%% complete"). + WithPriority(2). + WithStatus(models.StatusInProgress). + WithIssueType(models.TypeTask). + Build(), + } + + if err := t.app.Issues.CreateIssues(ctx, milestoneIssues, ""); err != nil { + return err + } + + t.setupIssue = models.NewBaseIssue(). + WithTitle("Q1 Release Milestone Tracking"). + WithDescription(milestoneTrackingDescription). + Build() + + return t.app.Issues.CreateIssue(ctx, t.setupIssue, "") +} + +func (t *MilestoneTrackingTask) Validate(ctx context.Context) (bool, error) { + return EndTaskWithTimeout(&t.done, "Milestone tracking task completed!", 5*time.Second) +} diff --git a/cmd/survey/tasks/priorityManagement.go b/cmd/survey/tasks/priorityManagement.go new file mode 100644 index 0000000..05cfbd5 --- /dev/null +++ b/cmd/survey/tasks/priorityManagement.go @@ -0,0 +1,120 @@ +package tasks + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/pkg/task" + taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui" + "github.com/charmbracelet/huh" +) + +const priorityManagementDescription = `You are tasked with managing issue priorities. + +A critical production issue has been reported. You need to rebalance the current sprint priorities: + +1. Review all current issues and their priorities +2. Identify the most urgent production issue +3. Reprioritize existing work to accommodate the urgent fix +4. Defer lower priority items if necessary +5. Update the team on priority changes via comments +6. Ensure the critical path is clear for the urgent fix + +The production database is experiencing intermittent connection failures affecting all users.` + +type PriorityManagementTask struct { + done bool + app *service.App + setupIssue *models.Issue +} + +func NewPriorityManagementTask(app *service.App) *PriorityManagementTask { + return &PriorityManagementTask{app: app, done: false} +} + +func (t *PriorityManagementTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/priority-task-stats.json") +} + +func (t *PriorityManagementTask) Details() taskui.TaskDetails { + return BaseDetails(). + WithTitle("Priority Management Task"). + WithDescription(priorityManagementDescription). + WithTimeToComplete("8m"). + WithDifficulty("Easy") +} + +func (t *PriorityManagementTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[int](). + Title("How many issues did you reprioritize or comment on?"). + Options( + huh.NewOption("1-2", 1), + huh.NewOption("3-4", 2), + huh.NewOption("5+", 3), + ), + ), + ) +} + +func (t *PriorityManagementTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + priorityIssues := []*models.Issue{ + models.NewIssueBuilder(). + WithTitle("Database connection failures"). + WithDescription("PRODUCTION CRITICAL: Intermittent DB connection failures affecting all users. Needs immediate attention."). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("UI theme updates"). + WithDescription("Update color scheme per new brand guidelines. Currently in progress but can wait."). + WithPriority(1). + WithStatus(models.StatusInProgress). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Feature: Dark mode"). + WithDescription("Add dark mode toggle to settings. Nice to have, can be deferred."). + WithPriority(2). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("API rate limiting"). + WithDescription("Add rate limiting to public API endpoints. Security enhancement."). + WithPriority(2). + WithStatus(models.StatusInProgress). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Documentation updates"). + WithDescription("Update API documentation for v2 endpoints. Can be deferred."). + WithPriority(3). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + } + + if err := t.app.Issues.CreateIssues(ctx, priorityIssues, ""); err != nil { + return err + } + + t.setupIssue = models.NewBaseIssue(). + WithTitle("Priority Rebalancing"). + WithDescription(priorityManagementDescription). + Build() + + return t.app.Issues.CreateIssue(ctx, t.setupIssue, "") +} + +func (t *PriorityManagementTask) Validate(ctx context.Context) (bool, error) { + return EndTaskWithTimeout(&t.done, "Priority management task completed!", 5*time.Second) +} diff --git a/cmd/survey/tasks/reportGeneration.go b/cmd/survey/tasks/reportGeneration.go new file mode 100644 index 0000000..60c9bac --- /dev/null +++ b/cmd/survey/tasks/reportGeneration.go @@ -0,0 +1,122 @@ +package tasks + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/pkg/task" + taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui" + "github.com/charmbracelet/huh" +) + +const reportGenerationDescription = `You are tasked with generating a project status report. + +The stakeholders need a weekly status update. Review the current project state and: + +1. Identify completed issues since last report +2. Count issues in progress and their status +3. Note any blocked items and blockers +4. Calculate velocity +5. Flag any risks or concerns +6. Update the project status + +Add summary comments to at least 3 key issues that stakeholders should know about.` + +type ReportGenerationTask struct { + done bool + app *service.App + setupIssue *models.Issue +} + +func NewReportGenerationTask(app *service.App) *ReportGenerationTask { + return &ReportGenerationTask{app: app, done: false} +} + +func (t *ReportGenerationTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/report-task-stats.json") +} + +func (t *ReportGenerationTask) Details() taskui.TaskDetails { + return BaseDetails(). + WithTitle("Status Report Generation"). + WithDescription(reportGenerationDescription). + WithTimeToComplete("10m"). + WithDifficulty("Easy") +} + +func (t *ReportGenerationTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[int](). + Title("What is the overall project status?"). + Options( + huh.NewOption("On Track", 1), + huh.NewOption("At Risk", 2), + huh.NewOption("Off Track", 3), + ), + ), + ) +} + +func (t *ReportGenerationTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + reportIssues := []*models.Issue{ + models.NewIssueBuilder(). + WithTitle("User login feature"). + WithDescription("Allow users to login with email/password."). + WithPriority(1). + WithStatus(models.StatusClosed). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Password reset"). + WithDescription("Email-based password reset flow. In Progress"). + WithPriority(2). + WithStatus(models.StatusInProgress). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Database optimization"). + WithDescription("Optimize slow queries identified in profiling."). + WithPriority(1). + WithStatus(models.StatusBlocked). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Mobile responsive design"). + WithDescription("Make UI work on mobile devices."). + WithPriority(2). + WithStatus(models.StatusClosed). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Third-party API integration"). + WithDescription("Waiting for vendor API documentation."). + WithPriority(1). + WithStatus(models.StatusBlocked). + WithIssueType(models.TypeTask). + Build(), + } + + for _, issue := range reportIssues { + if err := t.app.Issues.CreateIssue(ctx, issue, ""); err != nil { + return err + } + } + + t.setupIssue = models.NewBaseIssue(). + WithTitle("Weekly Status Report"). + WithDescription(reportGenerationDescription). + Build() + + return t.app.Issues.CreateIssue(ctx, t.setupIssue, "") +} + +func (t *ReportGenerationTask) Validate(ctx context.Context) (bool, error) { + return EndTaskWithTimeout(&t.done, "Report generation task completed!", 5*time.Second) +} diff --git a/cmd/survey/tasks/sprintPlanning.go b/cmd/survey/tasks/sprintPlanning.go new file mode 100644 index 0000000..4015cef --- /dev/null +++ b/cmd/survey/tasks/sprintPlanning.go @@ -0,0 +1,121 @@ +package tasks + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/pkg/task" + taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui" + "github.com/charmbracelet/huh" +) + +const sprintPlanningDescription = `You are tasked with sprint planning. + +A new sprint is starting and you need to organize the backlog. +You will be given a list of issues with different priorities and dependencies. + +Your task: +1. Review the backlog issues +2. Select which issues to include in the sprint +3. Update issue statuses to move items into the sprint +4. Address any blocked or dependent issues +5. Prioritize high-priority items + +The goal is to create a realistic sprint plan that delivers value while respecting team capacity.` + +type SprintPlanningTask struct { + done bool + app *service.App + setupIssue *models.Issue +} + +func NewSprintPlanningTask(app *service.App) *SprintPlanningTask { + return &SprintPlanningTask{app: app, done: false} +} + +func (t *SprintPlanningTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/sprint-planning-stats.json") +} + +func (t *SprintPlanningTask) Details() taskui.TaskDetails { + return BaseDetails(). + WithTitle("Sprint Planning Task"). + WithDescription(sprintPlanningDescription). + WithTimeToComplete("15m"). + WithDifficulty("Medium") +} + +func (t *SprintPlanningTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[int](). + Title("How many issues did you select for the sprint?"). + Options( + huh.NewOption("2-3 issues", 1), + huh.NewOption("4-5 issues", 2), + huh.NewOption("6+ issues", 3), + ), + ), + ) +} + +func (t *SprintPlanningTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + backlogIssues := []*models.Issue{ + models.NewIssueBuilder(). + WithTitle("Implement user authentication"). + WithDescription("Add login/logout functionality. Priority: High"). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Design database schema"). + WithDescription("Create tables for users and orders. Priority: High"). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Setup CI/CD pipeline"). + WithDescription("Configure automated testing and deployment. Currently blocked by server setup"). + WithPriority(2). + WithStatus(models.StatusBlocked). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Create API documentation"). + WithDescription("Document all REST endpoints. Priority: Low"). + WithPriority(3). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Implement search functionality"). + WithDescription("Depends on database schema. Priority: Medium"). + WithPriority(2). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + } + + if err := t.app.Issues.CreateIssues(ctx, backlogIssues, ""); err != nil { + return err + } + + t.setupIssue = models.NewBaseIssue(). + WithTitle("Sprint Planning - Week 1"). + WithDescription(sprintPlanningDescription). + Build() + + return t.app.Issues.CreateIssue(ctx, t.setupIssue, "") +} + +func (t *SprintPlanningTask) Validate(ctx context.Context) (bool, error) { + return EndTaskWithTimeout(&t.done, "Sprint planning task completed!", 5*time.Second) +} diff --git a/cmd/survey/tasks/stakeholderUpdate.go b/cmd/survey/tasks/stakeholderUpdate.go new file mode 100644 index 0000000..bf2f6b3 --- /dev/null +++ b/cmd/survey/tasks/stakeholderUpdate.go @@ -0,0 +1,115 @@ +package tasks + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/pkg/task" + taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui" + "github.com/charmbracelet/huh" +) + +const stakeholderUpdateDescription = `You are tasked with preparing stakeholder updates. + +A key stakeholder has requested an update on the progress of their requested features. +You need to: + +1. Identify issues related to the stakeholder's requests (marked with "stakeholder" in description) +2. Review the current status of each issue +3. Provide clear, non-technical status updates via comments +4. Highlight any blockers or delays +5. Set realistic expectations for delivery +6. Close completed items with completion notes + +The stakeholder is interested in the Dashboard Enhancement and Export Features specifically.` + +type StakeholderUpdateTask struct { + done bool + app *service.App + setupIssue *models.Issue +} + +func NewStakeholderUpdateTask(app *service.App) *StakeholderUpdateTask { + return &StakeholderUpdateTask{app: app, done: false} +} + +func (t *StakeholderUpdateTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/stakeholder-task-stats.json") +} + +func (t *StakeholderUpdateTask) Details() taskui.TaskDetails { + return BaseDetails(). + WithTitle("Stakeholder Update Task"). + WithDescription(stakeholderUpdateDescription). + WithTimeToComplete("10m"). + WithDifficulty("Easy") +} + +func (t *StakeholderUpdateTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[int](). + Title("How satisfied will the stakeholder be with this update?"). + Options( + huh.NewOption("Very satisfied", 1), + huh.NewOption("Satisfied", 2), + huh.NewOption("Neutral", 3), + huh.NewOption("Concerned", 4), + ), + ), + ) +} + +func (t *StakeholderUpdateTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + stakeholderIssues := []*models.Issue{ + models.NewIssueBuilder(). + WithTitle("Dashboard Enhancement - Charts"). + WithDescription("Add interactive charts to the main dashboard (stakeholder request). COMPLETED"). + WithPriority(1). + WithStatus(models.StatusClosed). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Dashboard Enhancement - Filters"). + WithDescription("Add date range filters to dashboard (stakeholder request). In Progress"). + WithPriority(2). + WithStatus(models.StatusInProgress). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Export to PDF"). + WithDescription("Allow exporting reports to PDF format (stakeholder request). In Progress"). + WithPriority(1). + WithStatus(models.StatusInProgress). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Export to Excel"). + WithDescription("Allow exporting data to Excel format (stakeholder request). BLOCKED - waiting for library approval"). + WithPriority(2). + WithStatus(models.StatusBlocked). + WithIssueType(models.TypeTask). + Build(), + } + + if err := t.app.Issues.CreateIssues(ctx, stakeholderIssues, ""); err != nil { + return err + } + + t.setupIssue = models.NewBaseIssue(). + WithTitle("Stakeholder Update Preparation"). + WithDescription(stakeholderUpdateDescription). + Build() + + return t.app.Issues.CreateIssue(ctx, t.setupIssue, "") +} + +func (t *StakeholderUpdateTask) Validate(ctx context.Context) (bool, error) { + return EndTaskWithTimeout(&t.done, "Stakeholder update task completed!", 5*time.Second) +} diff --git a/cmd/survey/tasks/teamCapacity.go b/cmd/survey/tasks/teamCapacity.go new file mode 100644 index 0000000..4790f80 --- /dev/null +++ b/cmd/survey/tasks/teamCapacity.go @@ -0,0 +1,127 @@ +package tasks + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/pkg/task" + taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui" + "github.com/charmbracelet/huh" +) + +const teamCapacityDescription = `You are tasked with managing team capacity. + +You have a team of 4 developers with varying skill and availability. +Review the upcoming sprint workload and: + +1. Review assigned issues and their priorities +2. Identify overloaded team members based on issue assignments +3. Rebalance workload to match capacity +4. Consider vacations and time off mentioned in issue descriptions +5. Ensure critical tasks have coverage + +Team member Alice is on vacation next week (mentioned in her issues). Bob can only work 50% time due to other commitments.` + +type TeamCapacityTask struct { + done bool + app *service.App + setupIssue *models.Issue +} + +func NewTeamCapacityTask(app *service.App) *TeamCapacityTask { + return &TeamCapacityTask{app: app, done: false} +} + +func (t *TeamCapacityTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/capacity-task-stats.json") +} + +func (t *TeamCapacityTask) Details() taskui.TaskDetails { + return BaseDetails(). + WithTitle("Team Capacity Management"). + WithDescription(teamCapacityDescription). + WithTimeToComplete("12m"). + WithDifficulty("Medium") +} + +func (t *TeamCapacityTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[int](). + Title("How many issues did you reassign or update?"). + Options( + huh.NewOption("0", 0), + huh.NewOption("1-2", 1), + huh.NewOption("3+", 2), + ), + ), + ) +} + +func (t *TeamCapacityTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + capacityIssues := []*models.Issue{ + models.NewIssueBuilder(). + WithTitle("Authentication module"). + WithDescription("Implement OAuth2 flow. Assigned to: Alice (on vacation next week)"). + WithPriority(1). + WithStatus(models.StatusInProgress). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Payment integration"). + WithDescription("Integrate Stripe API. Assigned to: Alice (on vacation next week)"). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Dashboard widgets"). + WithDescription("Create reusable widget components. Assigned to: Bob (50% capacity)"). + WithPriority(2). + WithStatus(models.StatusInProgress). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("API rate limiting"). + WithDescription("Add rate limiting middleware. Assigned to: Bob (50% capacity)"). + WithPriority(2). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Data migration"). + WithDescription("Migrate legacy data to new schema. Assigned to: Charlie (full capacity)"). + WithPriority(3). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + models.NewIssueBuilder(). + WithTitle("Bug fixes batch"). + WithDescription("Fix reported bugs from QA. Assigned to: Diana (full capacity)"). + WithPriority(1). + WithStatus(models.StatusOpen). + WithIssueType(models.TypeTask). + Build(), + } + + if err := t.app.Issues.CreateIssues(ctx, capacityIssues, ""); err != nil { + return err + } + + t.setupIssue = models.NewBaseIssue(). + WithTitle("Team Capacity Planning"). + WithDescription(teamCapacityDescription). + Build() + + return t.app.Issues.CreateIssue(ctx, t.setupIssue, "") +} + +func (t *TeamCapacityTask) Validate(ctx context.Context) (bool, error) { + return EndTaskWithTimeout(&t.done, "Team capacity task completed!", 5*time.Second) +} diff --git a/internal/models/issue.go b/internal/models/issue.go index cac4c34..1e692a0 100644 --- a/internal/models/issue.go +++ b/internal/models/issue.go @@ -15,7 +15,6 @@ func NewIssueBuilder() *IssueBuilder { func NewBaseIssue() *IssueBuilder { return NewIssueBuilder(). - WithID("pm-abc"). WithTitle("Basic Issue"). WithDescription("Basic Description"). WithStatus(StatusOpen).