@@ -26,15 +26,15 @@ func initInterfaces() map[string]task.Interface {
|
|||||||
return interfaces
|
return interfaces
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTasks(app *service.App) []task.Tasker {
|
func initTasks(app *service.App) map[string]task.Tasker {
|
||||||
var taskList []task.Tasker
|
taskMap := make(map[string]task.Tasker)
|
||||||
for _, name := range task.ListTasks() {
|
for _, name := range task.ListTasks() {
|
||||||
t, err := task.GetTasks(name, app)
|
t, err := task.GetTask(name, app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
taskList = append(taskList, t)
|
taskMap[name] = t
|
||||||
}
|
}
|
||||||
|
|
||||||
return taskList
|
return taskMap
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,5 +40,35 @@ func init() {
|
|||||||
task.RegisterTask("coding_task", func(app *service.App) task.Tasker {
|
task.RegisterTask("coding_task", func(app *service.App) task.Tasker {
|
||||||
return tasks.NewCodingTask(app)
|
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)
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,15 +32,14 @@ func runStartCmd(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.Flags().Changed("stage") {
|
if cmd.Flags().Changed("task") {
|
||||||
if surveyCmd.Task < 1 || surveyCmd.Task > len(surveyTasks) {
|
if surveyTask := surveyTasks[surveyCmd.Task]; surveyTask == nil {
|
||||||
return fmt.Errorf("invalid stage")
|
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 {
|
surveyTasks = map[string]task.Tasker{
|
||||||
return err
|
surveyCmd.Task: surveyTasks[surveyCmd.Task],
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := newIntroModel().Run(); err != nil {
|
if err := newIntroModel().Run(); err != nil {
|
||||||
@@ -53,7 +52,7 @@ func runStartCmd(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
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
|
var iNames []string
|
||||||
for name := range interfaces {
|
for name := range interfaces {
|
||||||
iNames = append(iNames, name)
|
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]
|
iNames[i], iNames[j] = iNames[j], iNames[i]
|
||||||
})
|
})
|
||||||
|
|
||||||
for i, t := range surveyTasks {
|
idx := 0
|
||||||
idx := i % len(iNames)
|
for _, t := range surveyTasks {
|
||||||
selected := interfaces[iNames[idx]]
|
iIdx := idx % len(iNames)
|
||||||
|
selected := interfaces[iNames[iIdx]]
|
||||||
|
|
||||||
if err := task.RunTask(ctx, t, selected, tasks.InterfaceToType(selected)); err != nil {
|
if err := task.RunTask(ctx, t, selected, tasks.InterfaceToType(selected)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
idx++
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
128
cmd/survey/tasks/backlogRefinement.go
Normal file
128
cmd/survey/tasks/backlogRefinement.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package tasks
|
package tasks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/repl"
|
"github.com/LazyBachelor/LazyPM/pkg/repl"
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/task"
|
"github.com/LazyBachelor/LazyPM/pkg/task"
|
||||||
@@ -90,3 +93,12 @@ func TUIQuestion(interfaceType task.InterfaceType, fields ...huh.Field) *huh.Gro
|
|||||||
}
|
}
|
||||||
return huh.NewGroup(fields...)
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/task"
|
"github.com/LazyBachelor/LazyPM/pkg/task"
|
||||||
@@ -14,8 +15,20 @@ import (
|
|||||||
|
|
||||||
const codingDescription = `You are tasked with writing a simple function.
|
const codingDescription = `You are tasked with writing a simple function.
|
||||||
|
|
||||||
Write a function that takes two integers and returns their sum.
|
This task will test your ability to write clean, working code.
|
||||||
The function should be named "Add" and be part of the "coding" package.`
|
|
||||||
|
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 + `
|
var textFileContent = codingDescription + `
|
||||||
Please write your code below this line!
|
Please write your code below this line!
|
||||||
@@ -23,11 +36,12 @@ Please write your code below this line!
|
|||||||
`
|
`
|
||||||
|
|
||||||
type CodingTask struct {
|
type CodingTask struct {
|
||||||
app *service.App
|
done bool
|
||||||
|
app *service.App
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCodingTask(app *service.App) *CodingTask {
|
func NewCodingTask(app *service.App) *CodingTask {
|
||||||
return &CodingTask{app: app}
|
return &CodingTask{app: app, done: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *CodingTask) Config() task.Config {
|
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 false, fmt.Errorf("the function does not contain a return statement")
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return EndTaskWithTimeout(&t.done, "Task completed!", 5*time.Second)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package tasks
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
"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.
|
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.
|
This task will test your ability to use the issue creation workflow effectively.
|
||||||
|
|
||||||
Assign this task to yourself and start creating the issue.
|
Your task:
|
||||||
Make sure to fill out all the necessary details, including the title, description, and assignee.`
|
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 {
|
type CreateIssueTask struct {
|
||||||
app *service.App
|
done bool
|
||||||
|
app *service.App
|
||||||
|
setupTask *models.Issue
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCreateIssueTask(app *service.App) *CreateIssueTask {
|
func NewCreateIssueTask(app *service.App) *CreateIssueTask {
|
||||||
return &CreateIssueTask{app: app}
|
return &CreateIssueTask{app: app, done: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *CreateIssueTask) Config() task.Config {
|
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 {
|
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 {
|
if err := ClearIssues(t.app); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
issue := models.NewBaseIssue().
|
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, &issue, "")
|
return t.app.Issues.CreateIssue(ctx, t.setupTask, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *CreateIssueTask) Validate(ctx context.Context) (bool, error) {
|
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
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.setupTask.Assignee == "" {
|
||||||
|
return false, fmt.Errorf("issue not assigned to self")
|
||||||
|
}
|
||||||
|
|
||||||
if len(issues) < 2 {
|
if len(issues) < 2 {
|
||||||
return false, fmt.Errorf("issue not created")
|
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 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)
|
||||||
}
|
}
|
||||||
|
|||||||
120
cmd/survey/tasks/dependencyManagement.go
Normal file
120
cmd/survey/tasks/dependencyManagement.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
124
cmd/survey/tasks/gitTask.go
Normal file
124
cmd/survey/tasks/gitTask.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
120
cmd/survey/tasks/issueTriage.go
Normal file
120
cmd/survey/tasks/issueTriage.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
120
cmd/survey/tasks/milestoneTracking.go
Normal file
120
cmd/survey/tasks/milestoneTracking.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
120
cmd/survey/tasks/priorityManagement.go
Normal file
120
cmd/survey/tasks/priorityManagement.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
122
cmd/survey/tasks/reportGeneration.go
Normal file
122
cmd/survey/tasks/reportGeneration.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
121
cmd/survey/tasks/sprintPlanning.go
Normal file
121
cmd/survey/tasks/sprintPlanning.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
115
cmd/survey/tasks/stakeholderUpdate.go
Normal file
115
cmd/survey/tasks/stakeholderUpdate.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
127
cmd/survey/tasks/teamCapacity.go
Normal file
127
cmd/survey/tasks/teamCapacity.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
14
go.mod
14
go.mod
@@ -4,6 +4,7 @@ go 1.25.6
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410
|
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/google/uuid v1.6.0
|
||||||
github.com/muesli/reflow v0.3.0
|
github.com/muesli/reflow v0.3.0
|
||||||
github.com/steveyegge/beads v0.49.6
|
github.com/steveyegge/beads v0.49.6
|
||||||
@@ -33,6 +34,8 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
@@ -51,16 +54,24 @@ require (
|
|||||||
github.com/cli/browser v1.3.0 // indirect
|
github.com/cli/browser v1.3.0 // indirect
|
||||||
github.com/clipperhouse/displaywidth v0.10.0 // indirect
|
github.com/clipperhouse/displaywidth v0.10.0 // indirect
|
||||||
github.com/clipperhouse/uax29/v2 v2.6.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/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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.12 // 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/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.5.0 // 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/haatos/goshipit v0.0.0-20260206030541-056850f43320 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // 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/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // 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/go-sqlite3 v0.30.5 // indirect
|
||||||
github.com/ncruces/julianday v1.0.0 // indirect
|
github.com/ncruces/julianday v1.0.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // 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/pkg/term v1.2.0-beta.2 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||||
github.com/sahilm/fuzzy v0.1.1 // 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/afero v1.15.0 // indirect
|
||||||
github.com/spf13/cast v1.10.0 // indirect
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.10 // 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/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
golang.org/x/tools v0.42.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
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
47
go.sum
47
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=
|
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 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
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 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
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 h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo=
|
||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ=
|
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 h1:kiKAPXTZE2Iaf8JbtM21r54A8bCNsncrfnokZZSrSDg=
|
||||||
github.com/a-h/templ v0.3.977/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
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 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
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 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
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/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 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
|
||||||
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
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/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 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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/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 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
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 h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
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/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 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
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 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||||
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
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 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
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=
|
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-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 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
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/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 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
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 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
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/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 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
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 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw=
|
||||||
github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
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=
|
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.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
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 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||||
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
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=
|
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/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 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
|
||||||
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
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 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
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/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/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.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 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
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 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
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 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-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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -4,11 +4,6 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
InterfaceType string
|
|
||||||
Task int
|
|
||||||
)
|
|
||||||
|
|
||||||
// RootCmd is the base command for the survey CLI.
|
// RootCmd is the base command for the survey CLI.
|
||||||
var RootCmd = &cobra.Command{
|
var RootCmd = &cobra.Command{
|
||||||
Use: "survey",
|
Use: "survey",
|
||||||
@@ -24,6 +19,4 @@ Your responses will be kept confidential and used solely for research purposes.`
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RootCmd.CompletionOptions.DisableDefaultCmd = true
|
RootCmd.CompletionOptions.DisableDefaultCmd = true
|
||||||
StartCmd.Flags().StringVarP(&InterfaceType, "interface", "i", "tui", "Specify interface.")
|
|
||||||
StartCmd.Flags().IntVarP(&Task, "task", "t", 1, "Run task directly")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,18 @@ package surveyCmd
|
|||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
var (
|
||||||
|
InterfaceType string
|
||||||
|
Task string
|
||||||
|
)
|
||||||
|
|
||||||
// StartCmd is the start command - RunE is set in cmd/survey/
|
// StartCmd is the start command - RunE is set in cmd/survey/
|
||||||
var StartCmd = &cobra.Command{
|
var StartCmd = &cobra.Command{
|
||||||
Use: "start",
|
Use: "start",
|
||||||
Short: "Start the user survey",
|
Short: "Start the user survey",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
StartCmd.Flags().StringVarP(&Task, "task", "t", "", "Specify task.")
|
||||||
|
StartCmd.Flags().StringVarP(&InterfaceType, "interface", "i", "", "Specify interface.")
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ func NewIssueBuilder() *IssueBuilder {
|
|||||||
|
|
||||||
func NewBaseIssue() *IssueBuilder {
|
func NewBaseIssue() *IssueBuilder {
|
||||||
return NewIssueBuilder().
|
return NewIssueBuilder().
|
||||||
WithID("pm-abc").
|
|
||||||
WithTitle("Basic Issue").
|
WithTitle("Basic Issue").
|
||||||
WithDescription("Basic Description").
|
WithDescription("Basic Description").
|
||||||
WithStatus(StatusOpen).
|
WithStatus(StatusOpen).
|
||||||
@@ -53,8 +52,8 @@ func (b *IssueBuilder) WithPriority(priority int) *IssueBuilder {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b IssueBuilder) Build() Issue {
|
func (b IssueBuilder) Build() *Issue {
|
||||||
return Issue{
|
return &Issue{
|
||||||
ID: b.ID,
|
ID: b.ID,
|
||||||
Title: b.Title,
|
Title: b.Title,
|
||||||
Description: b.Description,
|
Description: b.Description,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func RegisterTask(name string, constructor func(*service.App) Tasker) {
|
|||||||
taskRegistry[name] = constructor
|
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]
|
constructor, ok := taskRegistry[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("task %q not found", name)
|
return nil, fmt.Errorf("task %q not found", name)
|
||||||
|
|||||||
Reference in New Issue
Block a user