Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
098ca32
feat(serverless): add container + batchjob commands
hi-lei Apr 24, 2026
1c6d39a
feat(serverless): extract shared wizard steps, add batchjob wizard
hi-lei Apr 24, 2026
419825b
feat: serverless polish and CLI UX improvements
hi-lei May 15, 2026
73bd48f
add hint bar for all interactive command and add async live update fo…
hi-lei May 15, 2026
d92b4a6
chore(deps): bump verdagostack to v1.4.1, drop local replace
hi-lei May 30, 2026
413aa18
fix: adversarial-review fixes across factory, cmdutil, serverless
hi-lei May 30, 2026
2fae3d1
feat: remove deprecated HDD storage option from vm/volume create
hi-lei May 30, 2026
f5162ee
feat: register s3 unconditionally (GA); gate serverless pre-release
hi-lei May 30, 2026
389d2cf
feat(s3): resumable multipart upload, interactive pickers, profile fix
hi-lei May 30, 2026
959db38
feat(s3): interactive ls browser with multi-select download
hi-lei May 30, 2026
a8a9c94
feat(s3): interactive upload wizard, resume picker, same-host lock
hi-lei May 30, 2026
3ec81f2
feat(s3): show transfer rate on upload completion
hi-lei May 30, 2026
fb262c4
feat(s3): live upload progress line with bar, percent, and rate
hi-lei May 30, 2026
0b7f068
feat(s3): download progress bar + speed; unify transfer progress
hi-lei May 30, 2026
0dca0c6
feat(s3): resumable parallel download (chunk manifest, progress, rate)
hi-lei May 30, 2026
6f3f839
feat(s3): make ls-browser downloads resumable; document resume in cp …
hi-lei May 30, 2026
70603d9
feat(s3): show full local path on download completion
hi-lei May 30, 2026
3e9d2ab
fix(s3): don't bound the ls browser by --timeout (navigation + downlo…
hi-lei May 30, 2026
9d69c13
feat(s3): announce resuming download; comprehensive cp --help examples
hi-lei May 30, 2026
0e1c284
feat(s3): interactive TUI for mb/rb/rm/mv; ~/Downloads default; revie…
hi-lei Jun 1, 2026
7b69c60
docs(s3): document interactive modes; add S3 coverage to AI skills
hi-lei Jun 1, 2026
bb6cf01
add claude.md in serverless modlue
hi-lei Jun 1, 2026
cae929a
ci: track Go version from go.mod instead of a hard-coded pin
hi-lei Jun 1, 2026
72132c4
fix(deps): bump golang.org/x/sys to v0.45.0 (GO-2026-5024)
hi-lei Jun 1, 2026
358c504
fix(s3): annotate remaining int conversions for strict gosec scan
hi-lei Jun 1, 2026
677658e
fix(s3): preallocate multi-select slices (prealloc)
hi-lei Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 69 additions & 9 deletions .ai/skills/new-command.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,70 @@ if err != nil {

### 6. Interactive + Non-Interactive

Commands should support both modes:
Commands MUST support both modes:
- **Flags** for scripting/CI (non-interactive)
- **Prompter** for interactive use when flags are missing
- **Prompter** TUI for interactive use when the target is omitted on a terminal

**Implicit trigger.** Omitting the positional target on a TTY launches the
interactive flow; everything else stays one-shot. Gate it exactly like this:

```go
if len(args) >= wantArgs { // explicit args -> run directly
return run(cmd, f, ioStreams, opts, args...)
}
if f.AgentMode() { // agents get a structured error, never a prompt
return cmdutil.NewMissingFlagsError([]string{"s3://bucket/key"})
}
if !interactiveTTY(f) { // non-TTY or json/yaml output -> help, no silent prompt
return cmd.Help()
}
return runInteractive(cmd, f, ioStreams, opts, args)
```

`interactiveTTY(f)` == `IsStdoutTerminal() && !AgentMode() && OutputFormat() == "table"`.

**The hint bar is mandatory on every direct `Select` / `MultiSelect`.** Pass
`tui.WithShowHints(true)` (or `tui.WithMultiSelectShowHints(true)`) so the user
always sees the standard control legend:

```
↑/↓ navigate · type to filter · enter select · esc back · ctrl+c exit
```

(Wizard-engine step Loaders are the only exception — the composite renders its
own hint bar, so double-rendering is a bug.)

**Esc = soft back, Ctrl+C = hard exit — always. Never a confirmation dialog on
either.** Classify the prompter error; never bare-`return nil`:

```go
name := opts.Name
if name == "" {
name, err = prompter.TextInput(ctx, "Item name")
if err != nil {
return nil // User cancelled (Esc/Ctrl+C)
idx, err := f.Prompter().Select(ctx, "Pick one", labels, tui.WithShowHints(true))
if err != nil {
if cmdutil.IsPromptCancel(err) { // Esc OR Ctrl+C — flow doesn't care which
return nil
}
return err // a real I/O failure MUST propagate
}
```

Use `cmdutil.IsPromptInterrupt(err)` (Ctrl+C) and `cmdutil.IsPromptBack(err)`
(Esc) when the two must differ — e.g. a "Back to list / Exit" gate, or a wizard
where Esc steps back one prompt while Ctrl+C exits the whole flow.

**Multi-step wizards.** Model each prompt as its own step walked by an index
into a steps slice: Esc decrements the index (–1 on the first step ends the
flow = exit), Ctrl+C exits, success advances. Print a `Step N of M · Title`
header and a one-time intro banner so the user knows the plan. See
`cmd/s3/move_wizard.go` (`runMoveWizard` + `classifyNav`/`navIdx`) for the
reference pattern.

> **Pitfall that breaks Esc=back:** a shared picker that swallows cancellation
> into `("", nil)` (i.e. `if IsPromptCancel(err) { return "", nil }`) is fine for
> a top-level command, but inside a wizard it destroys back-navigation — the
> wizard can no longer tell Esc (go back) from Ctrl+C (exit) and Esc ends up
> exiting the whole flow. Wizard-facing pickers MUST return the **raw** prompter
> error so the step loop can classify it.

### 7. Output Conventions

- Normal output goes to `ioStreams.Out`
Expand All @@ -144,12 +194,22 @@ Delete and dangerous actions MUST confirm:

```go
confirmed, err := prompter.Confirm(ctx, fmt.Sprintf("Delete %s?", name))
if err != nil || !confirmed {
_, _ = fmt.Fprintln(ioStreams.ErrOut, "Cancelled.")
if err != nil {
if cmdutil.IsPromptCancel(err) { // Esc/Ctrl+C = clean cancel
_, _ = fmt.Fprintln(ioStreams.ErrOut, "Canceled.")
return nil
}
return err // real I/O failure must propagate
}
if !confirmed {
_, _ = fmt.Fprintln(ioStreams.ErrOut, "Canceled.")
return nil
}
```

(Note American spelling: `Canceled`. In `--agent` mode require `--yes` and never
prompt; without it, return `cmdutil.NewConfirmationRequiredError(<verb>)`.)

For dangerous actions, add warning styling:

```go
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.9"
go-version-file: go.mod
cache: true

- name: Add Go bin to PATH
Expand All @@ -47,7 +47,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.9"
go-version-file: go.mod
cache: true

- name: Run unit tests
Expand All @@ -63,7 +63,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.9"
go-version-file: go.mod
cache: true

- name: Build
Expand All @@ -79,7 +79,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.9"
go-version-file: go.mod
cache: true

- name: Add Go bin to PATH
Expand Down Expand Up @@ -107,7 +107,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.9"
go-version-file: go.mod
cache: true

- name: Check go mod tidy
Expand All @@ -129,7 +129,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.9"
go-version-file: go.mod
cache: true

- name: Add Go bin to PATH
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.9"
go-version-file: go.mod
cache: true

- name: Install git-cliff
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.9"
go-version-file: go.mod
cache: true

- name: Add Go bin to PATH
Expand Down Expand Up @@ -60,7 +60,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.9"
go-version-file: go.mod
cache: false

- name: Install gitleaks
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,5 @@ docs/plans/

# AI agent project-level configs (installed by users, not shipped)
.cursor/
.claude/skills/
.gitnexus
11 changes: 8 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ repos:
name: Organize Go imports (goimports)
- id: go-mod-tidy
name: Tidy Go modules (go mod tidy)
- id: go-unit-tests
name: Run Go unit tests
args: [-short]

- repo: local
hooks:
Expand All @@ -37,3 +34,11 @@ repos:
language: system
types: [go]
pass_filenames: false
# Replaces dnephin/pre-commit-golang's go-unit-tests, which hardcodes
# -timeout=30s and trips on legitimately slow tests (cmd/auth, options).
- id: go-unit-tests
name: Run Go unit tests
entry: make test
language: system
types: [go]
pass_filenames: false
48 changes: 48 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ Skipping these steps leads to pattern violations, broken dual-mode, and pricing
## Execution Rules

- **Follow existing patterns** — find the nearest similar command, match its structure
- **Senior comment style** — default to no comments. Add one only when the WHY is non-obvious (invariant, workaround, gotcha, evolution point). One line, identifier-first. Never narrate WHAT — well-named identifiers do that. Delete comments when the reason expires. See CLAUDE.md "Comment style — write like a senior"
- **Preserve dual mode** — every command must work interactive AND non-interactive. Never build one without the other
- **Interactive hint bar** — every direct `prompter.Select(...)` outside the wizard engine must pass `tui.WithShowHints(true)` (and the equivalent option on `MultiSelect`) so the prompt renders its key hints below the choices. Wizard steps are exempt — the composite already renders the hint bar
- **Ctrl+C exits immediately, no confirmation** — use `cmdutil.IsPromptCancel(err)` to detect either Esc or Ctrl+C and return cleanly. When a flow needs different behavior per key (e.g. a "Back to list / Exit" gate where Esc means back), split with `IsPromptInterrupt(err)` (Ctrl+C) and `IsPromptBack(err)` (Esc). Never show an "Exit?" confirmation dialog — Unix users expect Ctrl+C to be terminal
- **Never modify `verdagostack`** directly — describe needed changes for the maintainer
- **Commit only when asked** — don't auto-commit

Expand All @@ -50,6 +53,51 @@ Skipping these steps leads to pattern violations, broken dual-mode, and pricing
- [ ] `make test` passes (runs lint + unit tests)
- [ ] `--help` renders correctly for changed commands
- [ ] Interactive and non-interactive modes both work
- [ ] Interactive Selects pass `tui.WithShowHints(true)` so the hint bar renders
- [ ] No leftover debug code, TODOs, or commented-out blocks

If `make lint` reports issues, fix them *before* announcing completion. See `CLAUDE.md` § "Go House Style" for the patterns that prevent the common hits (http.NoBody, American spelling, reused constants, rangeValCopy, nilerr annotations, etc.).

<!-- gitnexus:start -->
# GitNexus — Code Intelligence

This project is indexed by GitNexus as **verda-cli** (7546 symbols, 19146 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.

> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.

## Always Do

- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.

## Never Do

- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.

## Resources

| Resource | Use for |
|----------|---------|
| `gitnexus://repo/verda-cli/context` | Codebase overview, check index freshness |
| `gitnexus://repo/verda-cli/clusters` | All functional areas |
| `gitnexus://repo/verda-cli/processes` | All execution flows |
| `gitnexus://repo/verda-cli/process/{name}` | Step-by-step execution trace |

## CLI

| Task | Read this skill file |
|------|---------------------|
| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` |
| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` |
| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` |
| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` |
| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` |
| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` |

<!-- gitnexus:end -->
59 changes: 59 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ The repo lints with `golangci-lint` via `make lint` (included in `make test`). T

`.golangci.yaml` is the authoritative list — all of the above come from linters enabled there.

### Comment style — write like a senior

- **Default to no comment.** Well-named identifiers carry the meaning. Add a comment only when the *why* is non-obvious: an invariant, a workaround, a gotcha a future reader would miss, an evolution point.
- **One line, identifier-first.** `// resolveContainerName: args[0], else picker; agent requires <name>.` beats a three-line paragraph.
- **Never narrate WHAT.** `// Loop over deployments and build labels` is noise — delete it.
- **Capture invariants, not history.** `// Describe still succeeds if status RPC fails.` is durable. `// Added for ticket VC-1234` rots — put it in the commit message.
- **Flag known evolution points.** `// if SDK gains json:"status", switch to explicit fields.` documents a future-failure mode so the next reader doesn't have to rediscover it.
- **Delete when the reason expires.** Workaround landed, gotcha fixed, SDK gap closed → remove the comment in the same commit.

### Every API-calling command MUST:

1. **Timeout context**: `ctx, cancel := context.WithTimeout(cmd.Context(), f.Options().Timeout)`
Expand All @@ -98,6 +107,12 @@ The repo lints with `golangci-lint` via `make lint` (included in `make test`). T
- Require `prompter.Confirm()` — return nil on cancel or Esc
- In agent mode (`f.AgentMode()`): require `--yes` flag, never auto-confirm

### Interactive commands MUST:

- **Show the hint bar at the bottom of every direct `Prompter.Select`** — pass `tui.WithShowHints(true)` to render `↑/↓ navigate · type to filter · enter select · esc back · ctrl+c exit` below the choices. Same for `MultiSelect` via the equivalent option. Wizard step Loaders are exempt — the wizard composite already renders its own hint bar; double-rendering is a bug.
- **Treat Ctrl+C as a hard exit, Esc as a soft back** — never show a confirmation dialog on either. Unix users expect Ctrl+C to be terminal; an "Exit?" prompt is friction, and confirmation dialogs themselves can be cancelled which makes the design contradictory. Use `cmdutil.IsPromptInterrupt(err)` for Ctrl+C and `cmdutil.IsPromptBack(err)` for Esc when the two need different handling (e.g. in a "Back to list / Exit" gate, Esc returns to the list while Ctrl+C exits the whole loop). Both are cleanly distinguishable via `cmdutil.IsPromptCancel(err)` if a flow doesn't care which key triggered it.
- **Use `cmdutil.IsPromptCancel(err)`** — never bare-`return nil` on prompter errors; distinguish clean Ctrl+C / Esc from real I/O failures and propagate the latter.

### Pricing — get this wrong and users get billed wrong:

- Instance `price_per_hour` from API is **per-unit** (per-GPU or per-vCPU)
Expand Down Expand Up @@ -154,3 +169,47 @@ If you modified a command, also verify:
## Other Agents

This repo targets Claude Code and OpenAI Codex. Claude auto-loads this file; Codex auto-loads `AGENTS.md` (execution contract). A `.cursor/rules/main.mdc` pointer exists for Cursor users but is not a primary target — if Cursor drops out of the stack, delete it rather than letting it drift.

<!-- gitnexus:start -->
# GitNexus — Code Intelligence

This project is indexed by GitNexus as **verda-cli** (7546 symbols, 19146 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.

> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.

## Always Do

- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.

## Never Do

- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.

## Resources

| Resource | Use for |
|----------|---------|
| `gitnexus://repo/verda-cli/context` | Codebase overview, check index freshness |
| `gitnexus://repo/verda-cli/clusters` | All functional areas |
| `gitnexus://repo/verda-cli/processes` | All execution flows |
| `gitnexus://repo/verda-cli/process/{name}` | Step-by-step execution trace |

## CLI

| Task | Read this skill file |
|------|---------------------|
| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` |
| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` |
| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` |
| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` |
| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` |
| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` |

<!-- gitnexus:end -->
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/verda-cloud/verda-cli

go 1.25.9
go 1.25.10

require (
charm.land/lipgloss/v2 v2.0.2
Expand All @@ -9,7 +9,7 @@ require (
github.com/spf13/pflag v1.0.10
github.com/spf13/viper v1.21.0
github.com/verda-cloud/verdacloud-sdk-go v1.4.2
github.com/verda-cloud/verdagostack v1.3.3
github.com/verda-cloud/verdagostack v1.4.1
go.yaml.in/yaml/v3 v3.0.4
)

Expand Down Expand Up @@ -99,6 +99,6 @@ require (
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.1 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/text v0.36.0 // indirect
)
Loading