refactor: make init wizard purely interactive, fix orchestrator UX#52
refactor: make init wizard purely interactive, fix orchestrator UX#52
Conversation
## init command - Stripped all config flags (--org, --repos, --repos-file, --enterprise) - Kept only --token and --env-file (PAT source) - Calls inner functions (scopeGitHub, scopeCopilot, listConnectionScopes, finalizeProject) directly instead of runConfigureScopes/runConfigureProjects - No duplicate banners or redundant DevLake discovery - Connection picker uses SelectMulti (no pre-selected defaults) - Scope configuration is plugin-aware: GitHub gets DORA patterns + repo selection, Copilot gets org/enterprise scope, unknown plugins get a 'not yet supported' message (future-proofing for GitLab, ADO) - Explicitly prompts for project name in Phase 4 ## configure full command - Same inner-function pattern: calls scopeGitHub/scopeCopilot and listConnectionScopes/finalizeProject directly - No duplicate banners from sub-commands - Connection picker uses SelectMulti (no pre-selected defaults) - Plugin-specific scope handling with switch statement
There was a problem hiding this comment.
Pull request overview
Refactors the CLI orchestrators (init and configure full) to use a more interactive, plugin-aware workflow, and updates terminal UX output (banners/emoji) while avoiding sub-command entrypoint delegation.
Changes:
- Removed most
initflags to make the wizard primarily interactive and added plugin-aware scoping/project setup. - Updated
configure fullto scope per-plugin via direct helper calls and removed default connection pre-selections. - Standardized various CLI outputs (Unicode banners/emoji) and adjusted connection selection behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| cmd/init.go | Makes init interactive, adds plugin-aware scoping + direct project finalization, and updates wizard UX output. |
| cmd/configure_full.go | Refactors full orchestrator to call inner helpers directly, removes default selections, and updates UX output and flow. |
Comments suppressed due to low confidence (5)
cmd/init.go:88
- Phase banner is missing the required blank line after the banner frame (terminal output standards rule #5). Add a trailing fmt.Println() after the 3 banner lines.
fmt.Println("\n╔══════════════════════════════════════╗")
fmt.Println("║ PHASE 2: Configure Connections ║")
fmt.Println("╚══════════════════════════════════════╝")
cmd/init.go:140
- Phase banner is missing the required blank line after the banner frame (terminal output standards rule #5). Add a trailing fmt.Println() after the 3 banner lines.
fmt.Println("\n╔══════════════════════════════════════╗")
fmt.Println("║ PHASE 3: Configure Scopes ║")
fmt.Println("╚══════════════════════════════════════╝")
cmd/init.go:193
- Phase banner is missing the required blank line after the banner frame (terminal output standards rule #5). Add a trailing fmt.Println() after the 3 banner lines.
fmt.Println("\n╔══════════════════════════════════════╗")
fmt.Println("║ PHASE 4: Project Setup ║")
fmt.Println("╚══════════════════════════════════════╝")
cmd/configure_full.go:104
- Phase banners should have a blank line on both sides (including after the banner). Add a trailing fmt.Println() after this 3-line banner to match terminal output standards and improve readability (see .github/instructions/terminal-output.instructions.md#phase-banners).
fmt.Println("\n╔══════════════════════════════════════╗")
fmt.Println("║ PHASE 1: Configure Connections ║")
fmt.Println("╚══════════════════════════════════════╝")
cmd/configure_full.go:138
- Phase banner is missing the required blank line after the banner frame (terminal output standards rule #5). Add a trailing fmt.Println() after the 3 banner lines.
fmt.Println("\n╔══════════════════════════════════════╗")
fmt.Println("║ PHASE 2: Configure Scopes ║")
fmt.Println("╚══════════════════════════════════════╝")
| fmt.Println("\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d") | ||
| fmt.Println("\n╔══════════════════════════════════════╗") | ||
| fmt.Println("║ PHASE 1: Deploy DevLake ║") | ||
| fmt.Println("╚══════════════════════════════════════╝") |
There was a problem hiding this comment.
Phase banners are missing the required blank line after the 3-line banner. Terminal output standards require an empty line after phase banners so the next output/prompt doesn’t jam against the frame (see .github/instructions/terminal-output.instructions.md#phase-banners). Add a trailing fmt.Println() after this banner.
This issue also appears in the following locations of the same file:
- line 86
- line 138
- line 191
| fmt.Println("╚══════════════════════════════════════╝") | |
| fmt.Println("╚══════════════════════════════════════╝") | |
| fmt.Println() |
| } | ||
|
|
||
| results, client, statePath, state, err := runConnectionsInternal(selectedDefs, initOrg, initEnterprise, initToken, initEnvFile, true) | ||
| results, _, _, _, err := runConnectionsInternal(selectedDefs, org, "", initToken, initEnvFile, true) |
There was a problem hiding this comment.
runInit already discovers DevLake (disc := devlake.Discover(cfgURL)) and constructs a client, but Phase 2 calls runConnectionsInternal which performs another devlake.Discover(cfgURL) and prints its own discovery step. This reintroduces redundant discovery/extra UX and contradicts the PR description (“no redundant discovery”). Consider refactoring connection creation to accept the already-discovered URL/client (or split runConnectionsInternal so init can skip discovery).
| fmt.Printf(" Deployment: %s\n", scopeOpts.DeployPattern) | ||
| fmt.Printf(" Production: %s\n", scopeOpts.ProdPattern) | ||
| fmt.Printf(" Incidents: label=%s\n", scopeOpts.IncidentLabel) |
There was a problem hiding this comment.
Sub-item indentation is inconsistent with the repo’s terminal output standards (3-space indent). These lines use 5 spaces before labels, which misaligns with other sub-items. Use a consistent " " indent for sub-items under a step (see .github/instructions/terminal-output.instructions.md#indentation).
| fmt.Printf(" Deployment: %s\n", scopeOpts.DeployPattern) | |
| fmt.Printf(" Production: %s\n", scopeOpts.ProdPattern) | |
| fmt.Printf(" Incidents: label=%s\n", scopeOpts.IncidentLabel) | |
| fmt.Printf(" Deployment: %s\n", scopeOpts.DeployPattern) | |
| fmt.Printf(" Production: %s\n", scopeOpts.ProdPattern) | |
| fmt.Printf(" Incidents: label=%s\n", scopeOpts.IncidentLabel) |
| fmt.Printf(" %-18s ID=%d %q\n", name, r.ConnectionID, r.Name) | ||
| } | ||
| fmt.Println(strings.Repeat("\u2500", 50)) | ||
| fmt.Println(strings.Repeat("─", 50)) |
There was a problem hiding this comment.
Terminal output standards require a blank line after separators. This summary prints a separator line, then ends with another separator but doesn’t print an empty line afterward, so subsequent output can run together. Add fmt.Println() after the closing separator (see .github/instructions/terminal-output.instructions.md#line-spacing-rules).
| fmt.Println(strings.Repeat("─", 50)) | |
| fmt.Println(strings.Repeat("─", 50)) | |
| fmt.Println() |
| // ── Phase 3: Create Project (call inner functions directly) ── | ||
| fmt.Println("\n╔══════════════════════════════════════╗") | ||
| fmt.Println("║ PHASE 3: Project Setup ║") | ||
| fmt.Println("╚══════════════════════════════════════╝") |
There was a problem hiding this comment.
Phase banner is missing the required blank line after the banner frame (terminal output standards rule #5). Add a trailing fmt.Println() after the 3 banner lines.
This issue also appears in the following locations of the same file:
- line 102
- line 136
| fmt.Println("╚══════════════════════════════════════╝") | |
| fmt.Println("╚══════════════════════════════════════╝") | |
| fmt.Println() |
| // ── Phase 1: Deploy ────────────────────────────────────────── | ||
| targets := []string{"local - Docker Compose on this machine", "azure - Azure Container Apps"} | ||
| choice := prompt.Select("Where would you like to deploy DevLake?", targets) | ||
| choice := prompt.Select("\nWhere would you like to deploy DevLake?", targets) |
There was a problem hiding this comment.
Avoid embedding leading newlines in prompt labels (e.g., "\nWhere would you like…"). The prompt package prints the label verbatim to stderr, so callers should emit spacing with a separate fmt.Println() before the prompt per terminal output standards; otherwise spacing becomes inconsistent and easy to double-up.
| choice := prompt.Select("\nWhere would you like to deploy DevLake?", targets) | |
| fmt.Println() | |
| choice := prompt.Select("Where would you like to deploy DevLake?", targets) |
| break | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
org can still be empty here when only the GitHub connection is selected and --org wasn’t provided (GitHub connections don’t prompt for org in runConnectionsInternal). Subsequent calls (scopeGitHub/finalizeProject) use org and will produce confusing output or failures. After resolving from results, require org (prompt for it in interactive mode or return a clear error in flag mode).
| } | |
| } | |
| if org == "" { | |
| return fmt.Errorf("no GitHub organization could be determined from flags or connections; please re-run with --org or configure a connection that specifies an organization") | |
| } |
Summary
Make
initfully interactive and fix both orchestrator commands (init,configure full) to call inner functions directly instead of sub-command entry points.init changes
--org,--repos,--repos-file,--enterprise— kept only--tokenand--env-filescopeGitHub(),scopeCopilot(),listConnectionScopes(),finalizeProject()directly — no duplicate banners, no redundant discoverySelectMultiinstead ofSelectMultiWithDefaults— user explicitly choosesswitch— GitHub gets DORA patterns + repo selection, Copilot gets org/enterprise scope, unknown plugins get a 'not yet supported' messageconfigure full changes
runConfigureScopes/runConfigureProjectscallsTesting
go buildcleango test ./...all passinit --helpshows only--tokenand--env-file