diff --git a/cmd/survey/init.go b/cmd/survey/init.go index 6c67f65..ef9e108 100644 --- a/cmd/survey/init.go +++ b/cmd/survey/init.go @@ -26,15 +26,15 @@ func initInterfaces() map[string]task.Interface { return interfaces } -func initTasks(app *service.App) []task.Tasker { - var taskList []task.Tasker +func initTasks(app *service.App) map[string]task.Tasker { + taskMap := make(map[string]task.Tasker) for _, name := range task.ListTasks() { - t, err := task.GetTasks(name, app) + t, err := task.GetTask(name, app) if err != nil { continue } - taskList = append(taskList, t) + taskMap[name] = t } - return taskList + return taskMap } diff --git a/cmd/survey/main.go b/cmd/survey/main.go index 850a620..8c1e4bb 100644 --- a/cmd/survey/main.go +++ b/cmd/survey/main.go @@ -40,5 +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 { + 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/runner.go b/cmd/survey/runner.go index 3b8fd1b..876ee42 100644 --- a/cmd/survey/runner.go +++ b/cmd/survey/runner.go @@ -32,15 +32,14 @@ func runStartCmd(cmd *cobra.Command, args []string) error { } } - if cmd.Flags().Changed("stage") { - if surveyCmd.Task < 1 || surveyCmd.Task > len(surveyTasks) { - return fmt.Errorf("invalid stage") + if cmd.Flags().Changed("task") { + if surveyTask := surveyTasks[surveyCmd.Task]; surveyTask == nil { + return fmt.Errorf("invalid task, valid are %v", task.ListTasks()) } - if err := task.RunTask(cmd.Context(), surveyTasks[surveyCmd.Task-1], - interfaces[surveyCmd.InterfaceType], tasks.InterfaceToType(interfaces[surveyCmd.InterfaceType])); err != nil { - return err + + surveyTasks = map[string]task.Tasker{ + surveyCmd.Task: surveyTasks[surveyCmd.Task], } - return nil } if err := newIntroModel().Run(); err != nil { @@ -53,7 +52,7 @@ func runStartCmd(cmd *cobra.Command, args []string) error { return nil } -func taskLoop(ctx context.Context, surveyTasks []task.Tasker, interfaces map[string]task.Interface) error { +func taskLoop(ctx context.Context, surveyTasks map[string]task.Tasker, interfaces map[string]task.Interface) error { var iNames []string for name := range interfaces { iNames = append(iNames, name) @@ -63,13 +62,15 @@ func taskLoop(ctx context.Context, surveyTasks []task.Tasker, interfaces map[str iNames[i], iNames[j] = iNames[j], iNames[i] }) - for i, t := range surveyTasks { - idx := i % len(iNames) - selected := interfaces[iNames[idx]] + idx := 0 + for _, t := range surveyTasks { + iIdx := idx % len(iNames) + selected := interfaces[iNames[iIdx]] if err := task.RunTask(ctx, t, selected, tasks.InterfaceToType(selected)); err != nil { return err } + idx++ } return nil } 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/base.go b/cmd/survey/tasks/base.go index 3432e5d..ee5a909 100644 --- a/cmd/survey/tasks/base.go +++ b/cmd/survey/tasks/base.go @@ -1,6 +1,9 @@ package tasks import ( + "fmt" + "time" + "github.com/LazyBachelor/LazyPM/internal/service" "github.com/LazyBachelor/LazyPM/pkg/repl" "github.com/LazyBachelor/LazyPM/pkg/task" @@ -90,3 +93,12 @@ func TUIQuestion(interfaceType task.InterfaceType, fields ...huh.Field) *huh.Gro } return huh.NewGroup(fields...) } + +func EndTaskWithTimeout(taskDone *bool, message string, timeout time.Duration) (bool, error) { + if !*taskDone { + *taskDone = true + return false, fmt.Errorf("%s", message) + } + time.Sleep(timeout) + return true, nil +} diff --git a/cmd/survey/tasks/codingTask.go b/cmd/survey/tasks/codingTask.go index 520e7ef..743c85b 100644 --- a/cmd/survey/tasks/codingTask.go +++ b/cmd/survey/tasks/codingTask.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strings" + "time" "github.com/LazyBachelor/LazyPM/internal/service" "github.com/LazyBachelor/LazyPM/pkg/task" @@ -14,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! @@ -23,11 +36,12 @@ Please write your code below this line! ` type CodingTask struct { - app *service.App + done bool + app *service.App } func NewCodingTask(app *service.App) *CodingTask { - return &CodingTask{app: app} + return &CodingTask{app: app, done: false} } func (t *CodingTask) Config() task.Config { @@ -99,5 +113,5 @@ func (t *CodingTask) Validate(ctx context.Context) (bool, error) { return false, fmt.Errorf("the function does not contain a return statement") } - return true, nil + return EndTaskWithTimeout(&t.done, "Task completed!", 5*time.Second) } diff --git a/cmd/survey/tasks/createIssue.go b/cmd/survey/tasks/createIssue.go index ee96e55..50daa3b 100644 --- a/cmd/survey/tasks/createIssue.go +++ b/cmd/survey/tasks/createIssue.go @@ -3,6 +3,7 @@ package tasks import ( "context" "fmt" + "time" "github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/internal/service" @@ -11,17 +12,26 @@ 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 the issue. -Make sure to fill out all the necessary details, including the title, description, and assignee.` +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 { - app *service.App + done bool + app *service.App + setupTask *models.Issue } func NewCreateIssueTask(app *service.App) *CreateIssueTask { - return &CreateIssueTask{app: app} + return &CreateIssueTask{app: app, done: false} } func (t *CreateIssueTask) Config() task.Config { @@ -37,15 +47,16 @@ func (t *CreateIssueTask) Questions(interfaceType task.InterfaceType) taskui.Que } func (t *CreateIssueTask) Setup(ctx context.Context) error { - // Clear existing issues to ensure a clean state for the task if err := ClearIssues(t.app); err != nil { return err } - issue := models.NewBaseIssue(). - WithTitle("Create a New Issue").WithDescription(description).Build() + t.setupTask = models.NewBaseIssue(). + WithTitle("Create a New Issue"). + WithDescription(description). + Build() - return t.app.Issues.CreateIssue(ctx, &issue, "") + return t.app.Issues.CreateIssue(ctx, t.setupTask, "") } func (t *CreateIssueTask) Validate(ctx context.Context) (bool, error) { @@ -54,6 +65,10 @@ func (t *CreateIssueTask) Validate(ctx context.Context) (bool, error) { return false, err } + if t.setupTask.Assignee == "" { + return false, fmt.Errorf("issue not assigned to self") + } + if len(issues) < 2 { return false, fmt.Errorf("issue not created") } @@ -78,5 +93,17 @@ func (t *CreateIssueTask) Validate(ctx context.Context) (bool, error) { return false, fmt.Errorf("issue description is empty") } - return true, nil + 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") + } + + if t.setupTask.Status != models.StatusClosed { + return false, fmt.Errorf("setup issue status is not closed") + } + + return EndTaskWithTimeout(&t.done, "Task completed!", 5*time.Second) } 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 new file mode 100644 index 0000000..d1b6d81 --- /dev/null +++ b/cmd/survey/tasks/gitTask.go @@ -0,0 +1,124 @@ +package tasks + +import ( + "context" + "errors" + "fmt" + "os" + "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" + "github.com/go-git/go-git/v6" +) + +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 + repo *git.Repository + done bool + + app *service.App +} + +func NewGitTask(app *service.App) *GitTask { + return &GitTask{app: app, done: false} +} + +func (t *GitTask) Config() task.Config { + return BaseConfig().WithStatisticsStoragePath("./.pm/git-task-stats.json") +} + +func (t *GitTask) Details() taskui.TaskDetails { + return BaseDetails().WithTitle("Git Task").WithDescription(gitTaskDescription) +} + +func (t *GitTask) Questions(interfaceType task.InterfaceType) taskui.Questions { + return BaseQuestions(interfaceType).With( + huh.NewGroup( + huh.NewSelect[string]().Title("What Git Interface did you use?"). + Options( + huh.Option[string]{Value: "cli", Key: "Command Line Interface"}, + huh.Option[string]{Value: "tui", Key: "Terminal User Interface"}, + huh.Option[string]{Value: "gui", Key: "Graphical User Interface"}, + ), + ), + ) +} + +func (t *GitTask) Setup(ctx context.Context) error { + if err := ClearIssues(t.app); err != nil { + return err + } + + var err error + t.repo, err = t.initRepo() + if err != nil { + return err + } + + os.WriteFile("./task/README.md", + []byte("This is a Git task. Please perform a Git operation here."), + 0o644) + + t.setupIssue = models.NewIssueBuilder(). + WithTitle("Git Task Setup Issue"). + WithDescription(gitTaskDescription). + WithIssueType(models.TypeTask). + Build() + + if err := t.app.Issues.CreateIssue(ctx, t.setupIssue, ""); err != nil { + return err + } + + return nil +} + +func (t *GitTask) Validate(ctx context.Context) (bool, error) { + + if t.setupIssue == nil { + return false, errors.New("setup issue not found") + } + + if t.setupIssue.Assignee == "" { + return false, fmt.Errorf("issue not assigned to self") + } + + if t.setupIssue.Status != models.StatusInProgress { + return false, fmt.Errorf("issue status is not in progress") + } + + // Git related validation can be added here, such as checking for commits, branches, etc. + + return EndTaskWithTimeout(&t.done, "Task completed!", 5*time.Second) +} + +func (t *GitTask) initRepo() (*git.Repository, error) { + repo, err := git.PlainInit("./task/.git/", true) + if err != nil { + if errors.Is(err, git.ErrTargetDirNotEmpty) { + repo, err = git.PlainOpen("./task/.git/") + if err != nil { + return nil, err + } + return repo, nil + } + return nil, err + } + return repo, nil +} 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..a90a135 --- /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/go.mod b/go.mod index bf16c65..a85c344 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.25.6 require ( charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 + github.com/go-git/go-git/v6 v6.0.0-20260222090600-424e9964d3a3 github.com/google/uuid v1.6.0 github.com/muesli/reflow v0.3.0 github.com/steveyegge/beads v0.49.6 @@ -33,6 +34,8 @@ require ( ) require ( + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect github.com/andybalholm/brotli v1.2.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect @@ -51,16 +54,24 @@ require ( github.com/cli/browser v1.3.0 // indirect github.com/clipperhouse/displaywidth v0.10.0 // indirect github.com/clipperhouse/uax29/v2 v2.6.0 // indirect + github.com/cloudflare/circl v1.6.1 // indirect + github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/emirpasic/gods v1.18.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fatih/color v1.18.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/go-git/gcfg/v2 v2.0.2 // indirect + github.com/go-git/go-billy/v6 v6.0.0-20260114122816-19306b749ecc // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/haatos/goshipit v0.0.0-20260206030541-056850f43320 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/kevinburke/ssh_config v1.5.0 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -80,10 +91,12 @@ require ( github.com/ncruces/go-sqlite3 v0.30.5 // indirect github.com/ncruces/julianday v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pkg/term v1.2.0-beta.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/sahilm/fuzzy v0.1.1 // indirect + github.com/sergi/go-diff v1.4.0 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect @@ -100,7 +113,6 @@ require ( golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect golang.org/x/tools v0.42.0 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index cfc7e34..596fcea 100644 --- a/go.sum +++ b/go.sum @@ -2,14 +2,22 @@ charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 h1:D9PbaszZYp charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410/go.mod h1:1qZyvvVCenJO2M1ac2mX0yyiIZJoZmDM4DG4s0udJkU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= +github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo= github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ= github.com/a-h/templ v0.3.977 h1:kiKAPXTZE2Iaf8JbtM21r54A8bCNsncrfnokZZSrSDg= github.com/a-h/templ v0.3.977/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -64,9 +72,13 @@ github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+Urai github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs= github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos= github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= +github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -74,6 +86,8 @@ github.com/donseba/go-htmx v1.12.1 h1:ZO9TWLyZYN3KL2s/N3ZasCf/B3dmX1xzmZoIkJIT+C github.com/donseba/go-htmx v1.12.1/go.mod h1:8PTAYvNKf8+QYis+DpAsggKz+sa2qljtMgvdAeNBh5s= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= @@ -84,8 +98,18 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= +github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo= +github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs= +github.com/go-git/go-billy/v6 v6.0.0-20260114122816-19306b749ecc h1:rhkjrnRkamkRC7woapp425E4CAH6RPcqsS9X8LA93IY= +github.com/go-git/go-billy/v6 v6.0.0-20260114122816-19306b749ecc/go.mod h1:X1oe0Z2qMsa9hkar3AAPuL9hu4Mi3ztXEjdqRhr6fcc= +github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20260122163445-0622d7459a67 h1:3hutPZF+/FBjR/9MdsLJ7e1mlt9pwHgwxMW7CrbmWII= +github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20260122163445-0622d7459a67/go.mod h1:xKt0pNHST9tYHvbiLxSY27CQWFwgIxBJuDrOE0JvbZw= +github.com/go-git/go-git/v6 v6.0.0-20260222090600-424e9964d3a3 h1:lgm4zCVktmdFyACShxJvn/WJZgtUA7ysOKFeVD4UZpY= +github.com/go-git/go-git/v6 v6.0.0-20260222090600-424e9964d3a3/go.mod h1:B88nWzfnhTlIikoJ4d84Nc9noKS5mJoA7SgDdkt0aPU= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/form/v4 v4.3.0 h1:OVttojbQv2WNCs4P+VnjPtrt/+30Ipw4890W3OaFlvk= @@ -98,6 +122,8 @@ github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy0 github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -106,8 +132,15 @@ github.com/haatos/goshipit v0.0.0-20260206030541-056850f43320 h1:sb7SfxZfN+U9OHC github.com/haatos/goshipit v0.0.0-20260206030541-056850f43320/go.mod h1:LFP8N8y5ORkifb+LZuOVNZYlJuV3WqdXCjxX5pGUaNI= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kevinburke/ssh_config v1.5.0 h1:3cPZmE54xb5j3G5xQCjSvokqNwU2uW+3ry1+PRLSPpA= +github.com/kevinburke/ssh_config v1.5.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -161,6 +194,8 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= +github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -169,8 +204,8 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -178,6 +213,8 @@ github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88ee github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= @@ -193,6 +230,7 @@ github.com/steveyegge/beads v0.49.6 h1:ac/SJBYuz+hUww07pbjLfhXw8RGNyIkslTbutqHzW github.com/steveyegge/beads v0.49.6/go.mod h1:yYUYUsF8GbLEylNiJMu2BSwAOCgRLrywfGOO8oKPmoA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -234,7 +272,10 @@ golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/commands/survey/root.go b/internal/commands/survey/root.go index b5e00a9..df5d9a2 100644 --- a/internal/commands/survey/root.go +++ b/internal/commands/survey/root.go @@ -4,11 +4,6 @@ import ( "github.com/spf13/cobra" ) -var ( - InterfaceType string - Task int -) - // RootCmd is the base command for the survey CLI. var RootCmd = &cobra.Command{ Use: "survey", @@ -24,6 +19,4 @@ Your responses will be kept confidential and used solely for research purposes.` func init() { RootCmd.CompletionOptions.DisableDefaultCmd = true - StartCmd.Flags().StringVarP(&InterfaceType, "interface", "i", "tui", "Specify interface.") - StartCmd.Flags().IntVarP(&Task, "task", "t", 1, "Run task directly") } diff --git a/internal/commands/survey/start.go b/internal/commands/survey/start.go index c987941..1a1de58 100644 --- a/internal/commands/survey/start.go +++ b/internal/commands/survey/start.go @@ -2,8 +2,18 @@ package surveyCmd import "github.com/spf13/cobra" +var ( + InterfaceType string + Task string +) + // StartCmd is the start command - RunE is set in cmd/survey/ var StartCmd = &cobra.Command{ Use: "start", Short: "Start the user survey", } + +func init() { + StartCmd.Flags().StringVarP(&Task, "task", "t", "", "Specify task.") + StartCmd.Flags().StringVarP(&InterfaceType, "interface", "i", "", "Specify interface.") +} diff --git a/internal/models/issue.go b/internal/models/issue.go index 813f6ac..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). @@ -53,8 +52,8 @@ func (b *IssueBuilder) WithPriority(priority int) *IssueBuilder { return b } -func (b IssueBuilder) Build() Issue { - return Issue{ +func (b IssueBuilder) Build() *Issue { + return &Issue{ ID: b.ID, Title: b.Title, Description: b.Description, diff --git a/pkg/task/register.go b/pkg/task/register.go index c98d75e..37067fa 100644 --- a/pkg/task/register.go +++ b/pkg/task/register.go @@ -40,7 +40,7 @@ func RegisterTask(name string, constructor func(*service.App) Tasker) { taskRegistry[name] = constructor } -func GetTasks(name string, app *service.App) (Tasker, error) { +func GetTask(name string, app *service.App) (Tasker, error) { constructor, ok := taskRegistry[name] if !ok { return nil, fmt.Errorf("task %q not found", name)