feat: Implement CLI (Cobra/BubbleTea) & PendingActionScheduler#14
feat: Implement CLI (Cobra/BubbleTea) & PendingActionScheduler#14
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements the v0.0.2 CLI functionality for Simili-Bot, adding a Cobra-based command structure with a BubbleTea interactive TUI, and introducing the PendingActionScheduler step to handle deferred actions like issue transfers.
Changes:
- Added PendingActionScheduler step to record pending actions to a local state directory when transfers cannot be executed immediately
- Implemented CLI using Cobra framework with a
processcommand to run the pipeline on individual issues - Added interactive TUI using BubbleTea to display real-time pipeline execution status
- Integrated E2E tests to verify the full pipeline execution flow
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/steps/pending_action_scheduler.go | Implements step to schedule deferred actions like transfers |
| internal/steps/pending_action_scheduler_test.go | Unit tests for the PendingActionScheduler step |
| internal/steps/register.go | Registers the new pending_action_scheduler step with the pipeline |
| cmd/simili/main.go | Refactored to use Cobra command framework |
| cmd/simili/commands/root.go | Cobra root command with global flags |
| cmd/simili/commands/process.go | Implements the process command for running pipelines on issues |
| cmd/simili/commands/pipeline_runner.go | Pipeline execution logic with status reporting wrapper |
| internal/tui/model.go | BubbleTea TUI model for interactive pipeline status display |
| tests/integration/e2e_test.go | End-to-end integration test for pipeline execution |
| go.mod | Added dependencies for Cobra and BubbleTea frameworks |
| go.sum | Updated dependency checksums |
| DOCS/0.0.2v/README.md | Documentation updates with CLI usage examples |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Mock mocks the pipeline.Step interface. | ||
| type MockStep struct { | ||
| NameFunc func() string | ||
| RunFunc func(ctx *pipeline.Context) error | ||
| } | ||
|
|
||
| func (m *MockStep) Name() string { | ||
| if m.NameFunc != nil { | ||
| return m.NameFunc() | ||
| } | ||
| return "mock_step" | ||
| } | ||
|
|
||
| func (m *MockStep) Run(ctx *pipeline.Context) error { | ||
| if m.RunFunc != nil { | ||
| return m.RunFunc(ctx) | ||
| } | ||
| return nil | ||
| } |
There was a problem hiding this comment.
The MockStep struct is defined in the e2e_test.go file but is not used in the test. The test only uses real pipeline steps. Consider either using the MockStep in the test or removing it if it's not needed. If it's intended for future test scenarios, add a comment explaining its purpose.
|
|
||
| func (s *statusReportingStep) Run(ctx *pipeline.Context) error { | ||
| s.statusChan <- tui.PipelineStatusMsg{Step: s.Name(), Status: "started", Message: "Starting..."} | ||
| time.Sleep(100 * time.Millisecond) // Artificial delay for visual effect |
There was a problem hiding this comment.
An artificial delay of 100ms is added for visual effect on every step execution. While this might improve the user experience during demos, it significantly slows down pipeline execution in production. Consider making this delay configurable (e.g., via a flag or environment variable) so it can be disabled in production or CI/CD environments.
| }() | ||
|
|
||
| if _, err := p.Run(); err != nil { | ||
| fmt.Printf("Alas, there's been an error: %v", err) |
There was a problem hiding this comment.
The error message uses informal language ("Alas, there's been an error") which is inconsistent with the rest of the codebase's error messages. Consider using a more standard format like "Error running TUI: %v" to maintain consistency.
| fmt.Printf("Alas, there's been an error: %v", err) | |
| fmt.Printf("Error running TUI: %v\n", err) |
| func NewPendingActionScheduler(deps *pipeline.Dependencies) *PendingActionScheduler { | ||
| // For now, we use a local directory for state. In the future, this could be a git branch. | ||
| return &PendingActionScheduler{ | ||
| stateDir: ".simili/pending", | ||
| } |
There was a problem hiding this comment.
The hardcoded state directory path ".simili/pending" could lead to issues in different deployment environments. Consider making this configurable through the Dependencies struct or environment variables, especially for production deployments where state should be persisted in a well-defined location.
| // Verify content | ||
| content, _ := os.ReadFile(expectedFile) | ||
| var action PendingAction | ||
| json.Unmarshal(content, &action) |
There was a problem hiding this comment.
The test doesn't verify error handling in the json.Unmarshal call. If unmarshaling fails, the test will silently continue with a zero-value action, which could lead to misleading test results. Consider adding error checking after unmarshaling to ensure the test validates the actual content written to the file.
| r.Register("pending_action_scheduler", func(deps *pipeline.Dependencies) (pipeline.Step, error) { | ||
| return NewPendingActionScheduler(deps), nil | ||
| }) |
There was a problem hiding this comment.
The newly registered "pending_action_scheduler" step is not included in any of the workflow presets (issue-triage, similarity-only, index-only). According to the PR description, this step handles deferred actions like transfers, which should logically be part of the "issue-triage" workflow after the "action_executor" step. Consider adding it to the appropriate workflow presets, particularly "issue-triage".
| ctx := context.Background() | ||
| pCtx := pipeline.NewContext(ctx, issue, cfg) |
There was a problem hiding this comment.
The pipeline uses context.Background() without any cancellation support. If a user presses Ctrl+C or quits the TUI, the pipeline goroutine will continue running in the background. Consider using a cancellable context and passing it through to support graceful shutdown when the user quits the TUI.
|
|
||
| return m, m.waitForActivity() | ||
|
|
||
| case ResultMsg: |
There was a problem hiding this comment.
When receiving a ResultMsg, the TUI immediately quits without displaying the final result output to the user. The View() method returns an empty string when quitting, so users won't see the pipeline's final result. Consider keeping the TUI visible for a moment to display the final output, or printing it to stdout before quitting.
| case ResultMsg: | |
| case ResultMsg: | |
| // Print the final output before quitting so the user can see the result. | |
| if msg.Output != "" { | |
| fmt.Println(msg.Output) | |
| } |
| prefix = "✗ " | ||
| style = errorStepStyle | ||
| case "skipped": | ||
| prefix = "- " |
There was a problem hiding this comment.
The View() method renders different prefixes based on step status, but when a step is "skipped", it uses "- " as the prefix which is the same visual indicator used for steps that haven't started yet (due to the default prefix being " " for non-current steps). This could be confusing. Consider using a different prefix for skipped steps (e.g., "○ " or "⊘ ") to clearly distinguish them from pending steps.
| prefix = "- " | |
| prefix = "○ " |
| } | ||
|
|
||
| // Marshal result to JSON | ||
| resultBytes, _ := json.MarshalIndent(pCtx.Result, "", " ") |
There was a problem hiding this comment.
Error from json.MarshalIndent is silently ignored. While marshaling a Result struct is unlikely to fail, it's better to handle the error explicitly, especially since this is the final output being sent to the user. If marshaling fails, the user will receive an empty output without any indication of what went wrong.
| resultBytes, _ := json.MarshalIndent(pCtx.Result, "", " ") | |
| resultBytes, err := json.MarshalIndent(pCtx.Result, "", " ") | |
| if err != nil { | |
| p.Send(tui.ResultMsg{Success: false, Output: err.Error()}) | |
| return | |
| } |
This commit fixes CI lint failures and addresses code quality issues identified in PR #14 review: - Fix unchecked json.Unmarshal error in pending_action_scheduler_test.go - Fix unchecked json.MarshalIndent error in pipeline_runner.go - Add timeout support to TUI waitForActivity to prevent hanging - Display final pipeline result before TUI quits - Make UI delay configurable via SIMILI_NO_UI_DELAY env var - Make pending action state directory configurable via env var - Add pending_action_scheduler to issue-triage workflow preset - Improve error logging for client initialization failures - Update error message for consistency - Improve visual distinction for skipped steps (○ instead of -) - Add documentation to MockStep explaining its purpose - Remove incomplete comment from process.go All tests pass and code builds successfully. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Overview
This PR completes the v0.0.2v CLI implementation phases.
Changes
Verification
Closes #5, #6