Skip to content

add interactive form to skipper add#13

Open
kwabenadarkwa wants to merge 1 commit intoJerryAgbesi:mainfrom
kwabenadarkwa:add_form
Open

add interactive form to skipper add#13
kwabenadarkwa wants to merge 1 commit intoJerryAgbesi:mainfrom
kwabenadarkwa:add_form

Conversation

@kwabenadarkwa
Copy link
Copy Markdown

@kwabenadarkwa kwabenadarkwa commented Apr 27, 2026

Summary

Running 'skipper add' with no positional args now opens a huh form prompting for alias, user, host name, and port. Alias stays optional and is derived from host[:port] when blank. Existing two-arg non-interactive flow is unchanged; passing only one arg now errors explicitly instead of failing on arg count.

Related issue

Closes #11 and #7

Checklist

  • Tests added or updated (if behavior changed)
  • make lint passes
  • go test ./... passes
  • README updated (if user-facing changes)

Summary by CodeRabbit

  • New Features
    • The add command now supports two modes: run skipper add with no arguments to launch an interactive form for entering SSH connection details, or use skipper add <alias> <user@host[:port]> for non-interactive mode as before.

Running 'skipper add' with no positional args now opens a huh form
prompting for alias, user, host name, and port. Alias stays optional
and is derived from host[:port] when blank. Existing two-arg
non-interactive flow is unchanged; passing only one arg now errors
explicitly instead of failing on arg count.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

📝 Walkthrough

Walkthrough

The pull request enhances the add command with an interactive form flow. When invoked with no arguments, users now see a form to input connection details; when invoked with two arguments, it maintains the existing non-interactive behavior. New UI module provides form rendering and validation; go.mod declares form-related dependencies as direct requires.

Changes

Cohort / File(s) Summary
Documentation
README.md
Updated command documentation to reflect dual add usage: no-argument interactive mode and parameterized non-interactive mode with example.
Command Handler
cmd/add.go
Refactored add command to support two execution paths: zero arguments launches interactive form via addform.Run, two arguments invoke existing non-interactive flow. Added printAddResult helper, updated argument validation from exact-2-args to allowing 0–2 args with error on 1 arg.
Command Tests
cmd/add_test.go
Renamed existing negative test to cover single-argument case; added three new tests exercising interactive path via addFormRunner mock: successful host persistence, automatic alias derivation, and cancellation handling.
Interactive Form Module
internal/ui/addform/form.go, internal/ui/addform/form_test.go
New form module defines Input/Result structs and Run function rendering Alias (optional), User (required), HostName (required), Port (optional 1–65535) fields with validation. Treats Ctrl+C/Esc as abort; parses and validates inputs; comprehensive test suite covers field validation, whitespace rejection, port range, and parsing behavior.
Dependency Management
go.mod
Moved charmbracelet/bubbles, charmbracelet/bubbletea, charmbracelet/huh, kevinburke/ssh_config, spf13/cobra from indirect to direct requires. Added indirect requires for catppuccin/go, charmbracelet/x/exp/strings, dustin/go-humanize, mitchellh/hashstructure/v2; removed clipperhouse/stringish.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as add Command
    participant Form as addform.Run
    participant Validator as Form Validators
    participant Config as sshconfig
    
    User->>CLI: skipper add (no args)
    CLI->>Form: addFormRunner(Input)
    Form->>User: Display form fields
    User->>Form: Enter alias, user@host, port
    Form->>Validator: Validate each field
    Validator-->>Form: Validation result
    alt Validation succeeds
        Form-->>CLI: Result{Alias, User, Hostname, Port}
        CLI->>Config: AddHost(Host{...})
        Config-->>CLI: success
        CLI->>User: Print add result
    else Validation fails or User cancels
        Form-->>CLI: Cancelled=true OR error
        CLI-->>User: Exit or error message
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • [feature] Replace --add flag with an add subcommand #7 — The linked issue #11 closes #7 by introducing the add subcommand; this PR extends that foundation by adding the interactive form variant to the same add command, providing the parameterized and interactive execution paths together.

Possibly related PRs

  • Shell Script & Additional Flags #1 — Introduces sshconfig.AddHost and target parsing logic that form the underlying host-add plumbing; this PR builds on that by routing both interactive and non-interactive add flows through the same persistent storage function.
  • feat(cmd)!: Add add sub command to replace previous --add flag #11 — Establishes the initial add subcommand with non-interactive two-argument execution; this PR extends it with zero-argument interactive form support while preserving existing command behavior.

Poem

🐰 A form blooms bright with fields so fair,
No args needed—just answer with care!
Bubbles and huh guide the way,
Interactive add holds court today! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR partially addresses issue #11's objectives. It implements the interactive form feature but does not complete the full scope: sshconfig.AddHost still returns (error) instead of (*Host, bool, error), the global --add flag removal is not shown, and CHANGELOG.md/.goreleaser.yaml updates are missing. Implement the remaining objectives from #11: modify sshconfig.AddHost signature to return (*Host, bool, error), remove the global --add flag, and add CHANGELOG.md and .goreleaser.yaml configuration updates.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding an interactive form to the add command, which is the primary feature of this PR.
Out of Scope Changes check ✅ Passed All changes are in-scope. The PR focuses on adding interactive form functionality to the add command, updating tests, dependencies, and documentation as expected for this feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/add.go (1)

29-42: ⚠️ Potential issue | 🟡 Minor

Check argument shape before resolving config path.

At Line 30, config path resolution runs before the one-arg guard. This can return a path error instead of the explicit argument error for invalid invocations.

💡 Suggested adjustment
 func runAdd(_ *cobra.Command, args []string) error {
-	path, err := resolveConfigPath(configPath)
-	if err != nil {
-		return err
-	}
-
 	switch len(args) {
 	case 2:
+		path, err := resolveConfigPath(configPath)
+		if err != nil {
+			return err
+		}
 		return addFromArgs(path, args[0], args[1])
 	case 1:
 		return fmt.Errorf("expected either no arguments or both <alias> and <user@host[:port]>")
 	default:
+		path, err := resolveConfigPath(configPath)
+		if err != nil {
+			return err
+		}
 		return addInteractive(path)
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/add.go` around lines 29 - 42, The function runAdd calls
resolveConfigPath(configPath) before validating args which lets path errors
surface for the one-argument invalid invocation; update runAdd to validate the
argument shape first (check len(args) and immediately return the explicit error
when len==1) and only call resolveConfigPath(configPath) for the branches that
need it (the two-arg branch that calls addFromArgs and the interactive branch
that calls addInteractive), keeping references to runAdd, resolveConfigPath,
addFromArgs, and addInteractive to locate the change.
🧹 Nitpick comments (1)
cmd/add_test.go (1)

53-149: Consider adding one test for form-runner error propagation.

You cover success, alias derivation, and cancellation well. A small extra case where addFormRunner returns a non-cancel error would round out interactive-path coverage.

As per coding guidelines **/*_test.go: "Add or update tests whenever behavior changes, and keep go test ./... green".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/add_test.go` around lines 53 - 149, Add a test (e.g.,
TestAddCommandInteractiveFormError) that sets addFormRunner to return a
non-cancel error (return addform.Result{}, errors.New("form failed")), runs the
command via rootCmd with args {"add","-c", path}, asserts that rootCmd.Execute()
returns that error (or at least a non-nil error), and verifies no config file
was created by checking sshconfig.ParseHosts(path) returns an error; reference
addFormRunner, rootCmd.Execute, and sshconfig.ParseHosts to locate the relevant
code paths to exercise.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@cmd/add.go`:
- Around line 29-42: The function runAdd calls resolveConfigPath(configPath)
before validating args which lets path errors surface for the one-argument
invalid invocation; update runAdd to validate the argument shape first (check
len(args) and immediately return the explicit error when len==1) and only call
resolveConfigPath(configPath) for the branches that need it (the two-arg branch
that calls addFromArgs and the interactive branch that calls addInteractive),
keeping references to runAdd, resolveConfigPath, addFromArgs, and addInteractive
to locate the change.

---

Nitpick comments:
In `@cmd/add_test.go`:
- Around line 53-149: Add a test (e.g., TestAddCommandInteractiveFormError) that
sets addFormRunner to return a non-cancel error (return addform.Result{},
errors.New("form failed")), runs the command via rootCmd with args {"add","-c",
path}, asserts that rootCmd.Execute() returns that error (or at least a non-nil
error), and verifies no config file was created by checking
sshconfig.ParseHosts(path) returns an error; reference addFormRunner,
rootCmd.Execute, and sshconfig.ParseHosts to locate the relevant code paths to
exercise.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 66e4ab69-7b90-432f-9045-ab7851de9b88

📥 Commits

Reviewing files that changed from the base of the PR and between a97140e and 736914f.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (6)
  • README.md
  • cmd/add.go
  • cmd/add_test.go
  • go.mod
  • internal/ui/addform/form.go
  • internal/ui/addform/form_test.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant