Merge pull request #29 from LazyBachelor/LPM-77

LPM-77 Task Drafts
This commit is contained in:
Robin Olsen
2026-02-25 06:45:59 -08:00
committed by GitHub
22 changed files with 1401 additions and 45 deletions

View File

@@ -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
} }

View File

@@ -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)
})
} }

View File

@@ -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
} }

View 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)
}

View File

@@ -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
}

View File

@@ -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)
} }

View File

@@ -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)
} }

View 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
View 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
}

View 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)
}

View 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)
}

View 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)
}

View 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)
}

View 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)
}

View 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)
}

View 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
View File

@@ -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
View File

@@ -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=

View File

@@ -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")
} }

View File

@@ -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.")
}

View File

@@ -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,

View File

@@ -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)